Introduce Parameter: Initial support

This commit is contained in:
Alexey Sedunov
2015-04-03 17:32:39 +03:00
parent 06960be3ad
commit 53a6827ad8
30 changed files with 711 additions and 13 deletions
@@ -676,6 +676,7 @@ fun main(args: Array<String>) {
model("refactoring/introduceVariable", extension = "kt", testMethod = "doIntroduceVariableTest")
model("refactoring/extractFunction", extension = "kt", testMethod = "doExtractFunctionTest")
model("refactoring/introduceProperty", extension = "kt", testMethod = "doIntroducePropertyTest")
model("refactoring/introduceParameter", extension = "kt", testMethod = "doIntroduceParameterTest")
}
testClass(javaClass<AbstractSelectExpressionForDebuggerTest>()) {
@@ -110,7 +110,7 @@ public abstract class CallableRefactoring<T: CallableDescriptor>(
protected abstract fun performRefactoring(descriptorsForChange: Collection<CallableDescriptor>)
public fun run() {
public fun run(): Boolean {
fun buttonPressed(code: Int, dialogButtons: List<String>, button: String): Boolean {
return code == dialogButtons indexOf button && button in dialogButtons
}
@@ -134,7 +134,7 @@ public abstract class CallableRefactoring<T: CallableDescriptor>(
if (kind == SYNTHESIZED) {
LOG.error("Change signature refactoring should not be called for synthesized member " + callableDescriptor)
return
return false
}
val closestModifiableDescriptors = getClosestModifiableDescriptors()
@@ -144,12 +144,12 @@ public abstract class CallableRefactoring<T: CallableDescriptor>(
?: Collections.singletonList(callableDescriptor)
if (ApplicationManager.getApplication()!!.isUnitTestMode()) {
performRefactoring(deepestSuperDeclarations)
return
return true
}
if (closestModifiableDescriptors.size() == 1 && deepestSuperDeclarations == closestModifiableDescriptors) {
performRefactoring(closestModifiableDescriptors)
return
return true
}
val isSingleFunctionSelected = closestModifiableDescriptors.size() == 1
@@ -165,8 +165,10 @@ public abstract class CallableRefactoring<T: CallableDescriptor>(
}
else -> {
//do nothing
return false
}
}
return true
}
}
@@ -3,6 +3,7 @@ cannot.refactor.not.expression.to.extract=Cannot find an expression to extract
expressions.title=Expressions
introduce.variable=Introduce Variable
introduce.property=Introduce Property
introduce.parameter=Introduce Parameter
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.introduceParameter.KotlinIntroduceParameterHandler;
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;
@@ -40,6 +41,12 @@ public class JetRefactoringSupportProvider extends RefactoringSupportProvider {
return new KotlinIntroduceVariableHandler();
}
@Nullable
@Override
public RefactoringActionHandler getIntroduceParameterHandler() {
return new KotlinIntroduceParameterHandler();
}
@NotNull
public RefactoringActionHandler getIntroducePropertyHandler() {
return new KotlinIntroducePropertyHandler();
@@ -56,8 +56,8 @@ public fun runChangeSignature(project: Project,
configuration: JetChangeSignatureConfiguration,
bindingContext: BindingContext,
defaultValueContext: PsiElement,
commandName: String? = null) {
JetChangeSignature(project, functionDescriptor, configuration, bindingContext, defaultValueContext, commandName).run()
commandName: String? = null): Boolean {
return JetChangeSignature(project, functionDescriptor, configuration, bindingContext, defaultValueContext, commandName).run()
}
public class JetChangeSignature(project: Project,
@@ -33,6 +33,10 @@ public class JetMutableMethodDescriptor(val original: JetMethodDescriptor): JetM
parameters.add(parameter)
}
public fun setParameter(index: Int, parameter: JetParameterInfo) {
parameters[index] = parameter
}
public fun removeParameter(index: Int) {
parameters.remove(index)
}
@@ -156,8 +156,15 @@ public class JetFunctionCallUsage extends JetUsageInfo<JetCallElement> {
? psiFactory.createArgument(oldArgument.getArgumentExpression())
: oldArgument.asElement());
}
else if (parameterInfo.getDefaultValueForCall().isEmpty())
newArgument.delete();
// TODO: process default arguments in the middle
else if (parameterInfo.getDefaultValueForCall().isEmpty()) {
if (parameterInfo.getDefaultValueForParameter() != null) {
JetPsiUtil.deleteElementWithDelimiters(newArgument);
}
else {
newArgument.delete();
}
}
}
List<JetFunctionLiteralArgument> lambdaArguments = element.getFunctionLiteralArguments();
@@ -0,0 +1,239 @@
/*
* 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.introduceParameter
import com.intellij.codeInsight.template.impl.TemplateManagerImpl
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.EditorFactory
import com.intellij.openapi.editor.colors.EditorColors
import com.intellij.openapi.editor.event.DocumentAdapter
import com.intellij.openapi.editor.event.DocumentEvent
import com.intellij.openapi.editor.ex.EditorEx
import com.intellij.openapi.editor.impl.DocumentMarkupModel
import com.intellij.openapi.editor.markup.EffectType
import com.intellij.openapi.editor.markup.HighlighterTargetArea
import com.intellij.openapi.editor.markup.MarkupModel
import com.intellij.openapi.editor.markup.TextAttributes
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import com.intellij.refactoring.rename.inplace.InplaceRefactoring
import com.intellij.ui.DottedBorder
import com.intellij.ui.JBColor
import org.jetbrains.kotlin.idea.JetFileType
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.refactoring.changeSignature.*
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinInplaceVariableIntroducer
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.application.executeCommand
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.psi.JetExpression
import org.jetbrains.kotlin.psi.JetParameter
import org.jetbrains.kotlin.psi.JetPsiFactory
import org.jetbrains.kotlin.psi.JetPsiUtil
import org.jetbrains.kotlin.psi.psiUtil.getValueParameterList
import org.jetbrains.kotlin.psi.psiUtil.getValueParameters
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.types.JetType
import java.awt.BorderLayout
import java.awt.Color
import java.util.ArrayList
import java.util.LinkedHashSet
import javax.swing.BorderFactory
import javax.swing.JPanel
import javax.swing.border.EmptyBorder
import javax.swing.border.LineBorder
import kotlin.properties.Delegates
public class KotlinInplaceParameterIntroducer(
val descriptor: IntroduceParameterDescriptor,
editor: Editor,
project: Project
): KotlinInplaceVariableIntroducer<JetParameter>(
descriptor.addedParameter,
editor,
project,
INTRODUCE_PARAMETER,
JetExpression.EMPTY_ARRAY,
null,
false,
descriptor.addedParameter,
false,
true,
descriptor.parameterType,
false
) {
companion object {
private val LOG = Logger.getInstance(javaClass<KotlinInplaceParameterIntroducer>())
}
object PreviewDecorator {
protected val textAttributes: TextAttributes = with(TextAttributes()) {
setEffectType(EffectType.ROUNDED_BOX)
setEffectColor(JBColor.RED)
this
}
fun applyToRange(range: TextRange, markupModel: MarkupModel) {
markupModel.addRangeHighlighter(range.getStartOffset(),
range.getEndOffset(),
0,
textAttributes,
HighlighterTargetArea.EXACT_RANGE
)
}
}
private var previewer: EditorEx? = null
private fun updatePreview(currentName: String?, currentType: String?) {
with (descriptor) {
var addedRange: TextRange? = null
val builder = StringBuilder()
builder.append(callable.getName())
val parameters = callable.getValueParameters()
builder.append("(")
for (i in parameters.indices) {
val parameter = parameters[i]
val parameterText = if (parameter == addedParameter){
val parameterName = currentName ?: parameter.getName()
val parameterType = currentType ?: parameter.getTypeReference()!!.getText()
val modifier = if (valVar != JetValVar.None) "${valVar.name} " else ""
"$modifier$parameterName: $parameterType"
}
else parameter.getText()
builder.append(parameterText)
val range = TextRange(builder.length() - parameterText.length(), builder.length())
if (parameter == addedParameter) {
addedRange = range
}
if (i < parameters.lastIndex) {
builder.append(", ")
}
}
builder.append(")")
if (addedRange == null) {
LOG.error("Added parameter not found: ${JetPsiUtil.getElementTextWithContext(callable)}")
}
val document = previewer!!.getDocument()
runWriteAction { document.setText(builder.toString()) }
val markupModel = DocumentMarkupModel.forDocument(document, myProject, true)
markupModel.removeAllHighlighters()
addedRange?.let { PreviewDecorator.applyToRange(it, markupModel) }
}
revalidate()
}
override fun initPanelControls() {
addPanelControl {
val previewer = EditorFactory.getInstance().createEditor(EditorFactory.getInstance().createDocument(""),
myProject,
JetFileType.INSTANCE,
true) as EditorEx
this.previewer = previewer
previewer.setOneLineMode(true)
with(previewer.getSettings()) {
setAdditionalLinesCount(0)
setAdditionalColumnsCount(1)
setRightMarginShown(false)
setFoldingOutlineShown(false)
setLineNumbersShown(false)
setLineMarkerAreaShown(false)
setIndentGuidesShown(false)
setVirtualSpace(false)
setLineCursorWidth(1)
}
previewer.setHorizontalScrollbarVisible(false)
previewer.setVerticalScrollbarVisible(false)
previewer.setCaretEnabled(false)
val bg = previewer.getColorsScheme().getColor(EditorColors.CARET_ROW_COLOR)
previewer.setBackgroundColor(bg)
previewer.setBorder(BorderFactory.createCompoundBorder(DottedBorder(Color.gray), LineBorder(bg, 2)))
updatePreview(null, null)
val previewerPanel = JPanel(BorderLayout())
previewerPanel.add(previewer.getComponent(), BorderLayout.CENTER)
previewerPanel.setBorder(EmptyBorder(2, 2, 6, 2))
previewerPanel
}
}
private var myDocumentAdapter: DocumentAdapter? = null
fun startRefactoring(suggestedNames: LinkedHashSet<String>): Boolean {
if (!performInplaceRefactoring(suggestedNames)) return false
myDocumentAdapter = object : DocumentAdapter() {
override fun documentChanged(e: DocumentEvent?) {
if (previewer == null) return
val templateState = TemplateManagerImpl.getTemplateState(myEditor)
if (templateState != null) {
val name = templateState.getVariableValue(KotlinInplaceVariableIntroducer.PRIMARY_VARIABLE_NAME)?.getText()
val typeRefText = templateState.getVariableValue(KotlinInplaceVariableIntroducer.TYPE_REFERENCE_VARIABLE_NAME)?.getText()
updatePreview(name, typeRefText)
}
}
}
myEditor.getDocument().addDocumentListener(myDocumentAdapter!!)
return true
}
override fun finish(success: Boolean) {
super.finish(success)
myDocumentAdapter?.let { myEditor.getDocument().removeDocumentListener(it) }
}
override fun checkLocalScope(): PsiElement? {
return descriptor.callable
}
override fun performRefactoring(): Boolean {
descriptor.performRefactoring()
return true
}
override fun performCleanup() {
runWriteAction { JetPsiUtil.deleteElementWithDelimiters(descriptor.addedParameter) }
}
override fun releaseResources() {
super.releaseResources()
previewer?.let {
EditorFactory.getInstance().releaseEditor(it)
previewer = null
}
}
}
@@ -0,0 +1,211 @@
/*
* 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.introduceParameter
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.core.refactoring.JetNameSuggester
import org.jetbrains.kotlin.idea.refactoring.JetNameValidatorImpl
import org.jetbrains.kotlin.idea.refactoring.JetRefactoringBundle
import org.jetbrains.kotlin.idea.refactoring.changeSignature.*
import org.jetbrains.kotlin.idea.refactoring.introduce.KotlinIntroduceHandlerBase
import org.jetbrains.kotlin.idea.refactoring.introduce.selectElementsWithTargetParent
import org.jetbrains.kotlin.idea.refactoring.introduce.showErrorHintByKey
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.application.executeCommand
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.idea.util.approximateWithResolvableType
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.getValueParameterList
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.scopes.JetScopeUtils
import org.jetbrains.kotlin.types.JetType
import kotlin.test.fail
public data class IntroduceParameterDescriptor(
val originalExpression: JetExpression,
val callable: JetNamedDeclaration,
val callableDescriptor: FunctionDescriptor,
val addedParameter: JetParameter,
val parameterType: JetType
) {
val valVar: JetValVar
init {
valVar = if (callable is JetClass) {
val modifierIsUnnecessary: (PsiElement) -> Boolean = {
when {
it.getParent() != callable.getBody() ->
false
it is JetClassInitializer ->
true
it is JetProperty && it.getInitializer()?.getTextRange()?.intersects(originalExpression.getTextRange()) ?: false ->
true
else ->
false
}
}
if (originalExpression.parents().any(modifierIsUnnecessary)) JetValVar.None else JetValVar.Val
}
else JetValVar.None
}
}
fun IntroduceParameterDescriptor.performRefactoring() {
runWriteAction {
JetPsiUtil.deleteElementWithDelimiters(addedParameter)
val config = object: JetChangeSignatureConfiguration {
override fun configure(originalDescriptor: JetMethodDescriptor, bindingContext: BindingContext): JetMethodDescriptor {
return originalDescriptor.modify {
val parameterInfo = JetParameterInfo(name = addedParameter.getName()!!,
type = parameterType,
defaultValueForParameter = JetPsiUtil.deparenthesize(originalExpression),
valOrVar = valVar)
parameterInfo.currentTypeText = addedParameter.getTypeReference()?.getText() ?: "Any"
addParameter(parameterInfo)
}
}
override fun performSilently(affectedFunctions: Collection<PsiElement>): Boolean = true
}
if (runChangeSignature(callable.getProject(), callableDescriptor, config, callable.analyze(), callable, INTRODUCE_PARAMETER)) {
originalExpression.replace(JetPsiFactory(callable).createSimpleName(addedParameter.getName()!!))
}
}
}
public class KotlinIntroduceParameterHandler: KotlinIntroduceHandlerBase() {
public fun invoke(project: Project, editor: Editor, expression: JetExpression, targetParent: JetNamedDeclaration) {
val psiFactory = JetPsiFactory(project)
val parameterList = targetParent.getValueParameterList()
val context = expression.analyze()
val descriptor = context[BindingContext.DECLARATION_TO_DESCRIPTOR, targetParent]
val functionDescriptor: FunctionDescriptor =
when (descriptor) {
is FunctionDescriptor -> descriptor : FunctionDescriptor
is ClassDescriptor -> descriptor.getUnsubstitutedPrimaryConstructor()
else -> null
} ?: throw AssertionError("Unexpected element type: ${JetPsiUtil.getElementTextWithContext(targetParent)}")
val expressionType = context[BindingContext.EXPRESSION_TYPE, expression] ?: KotlinBuiltIns.getInstance().getAnyType()
val parameterType = expressionType.approximateWithResolvableType(JetScopeUtils.getResolutionScope(targetParent, context), false)
val validatorContainer =
when (targetParent) {
is JetFunction -> targetParent.getBodyExpression()
is JetClass -> targetParent.getBody()
else -> null
} ?: throw AssertionError("Body element is not found: ${JetPsiUtil.getElementTextWithContext(targetParent)}")
val nameValidator = JetNameValidatorImpl(validatorContainer, null, JetNameValidatorImpl.Target.PROPERTIES)
val suggestedNames = linkedSetOf(*JetNameSuggester.suggestNames(parameterType, nameValidator, "p"))
project.executeCommand(INTRODUCE_PARAMETER) {
val renderedType = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(parameterType)
val newParameter = psiFactory.createParameter("${suggestedNames.first()}: $renderedType")
val addedParameter = runWriteAction {
val newParameterList =
if (parameterList == null) {
val klass = targetParent as? JetClass
val anchor = klass?.getTypeParameterList() ?: klass?.getNameIdentifier()
assert(anchor != null, "Invalid declaration: ${JetPsiUtil.getElementTextWithContext(targetParent)}")
val constructor = targetParent.addAfter(psiFactory.createPrimaryConstructor(), anchor) as JetPrimaryConstructor
constructor.getValueParameterList()!!
}
else parameterList
val lastParameter = newParameterList.getChildren().lastOrNull { it is JetParameter } as? JetParameter
if (lastParameter != null) {
val comma = newParameterList.addAfter(psiFactory.createComma(), lastParameter)
newParameterList.addAfter(newParameter, comma) as JetParameter
}
else {
val singleParameterList = psiFactory.createParameterList("(${newParameter.getText()})")
(newParameterList.replace(singleParameterList) as JetParameterList).getParameters().first()
}
}
val introduceParameterDescriptor =
IntroduceParameterDescriptor(JetPsiUtil.deparenthesize(expression)!!,
targetParent,
functionDescriptor,
addedParameter,
parameterType)
if (editor.getSettings().isVariableInplaceRenameEnabled() && !ApplicationManager.getApplication().isUnitTestMode()) {
with(PsiDocumentManager.getInstance(project)) {
commitDocument(editor.getDocument())
doPostponedOperationsAndUnblockDocument(editor.getDocument())
}
if (!KotlinInplaceParameterIntroducer(introduceParameterDescriptor, editor, project).startRefactoring(suggestedNames)) {
introduceParameterDescriptor.performRefactoring()
}
}
else {
introduceParameterDescriptor.performRefactoring()
}
}
}
override fun invoke(project: Project, editor: Editor, file: PsiFile, dataContext: DataContext?) {
if (file !is JetFile) return
selectElementsWithTargetParent(
operationName = INTRODUCE_PARAMETER,
editor = editor,
file = file,
getContainers = { elements, parent ->
parent.parents(withItself = false)
.filter {
((it is JetClass && !it.isTrait() && it !is JetEnumEntry) || it is JetNamedFunction || it is JetSecondaryConstructor) &&
((it as JetNamedDeclaration).getValueParameterList() != null || it.getNameIdentifier() != null)
}
.toList()
},
continuation = { elements, targetParent ->
val expression = ((elements.singleOrNull() as? JetBlockExpression)?.getStatements() ?: elements).singleOrNull()
if (expression is JetExpression) {
invoke(project, editor, expression, targetParent as JetNamedDeclaration)
}
else {
showErrorHintByKey(project, editor, "cannot.refactor.no.expression", INTRODUCE_PARAMETER)
}
}
)
}
override fun invoke(project: Project, elements: Array<out PsiElement>, dataContext: DataContext?) {
fail("$INTRODUCE_PARAMETER can only be invoked from editor")
}
}
val INTRODUCE_PARAMETER: String = JetRefactoringBundle.message("introduce.property")
@@ -26,6 +26,7 @@ import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.markup.RangeHighlighter;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.Balloon;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Pass;
import com.intellij.openapi.util.Ref;
@@ -35,6 +36,7 @@ import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
import com.intellij.psi.search.SearchScope;
import com.intellij.refactoring.introduce.inplace.InplaceVariableIntroducer;
import com.intellij.ui.NonFocusableCheckBox;
import com.intellij.util.ui.PositionTracker;
import kotlin.Function0;
import kotlin.Function1;
import kotlin.KotlinPackage;
@@ -56,6 +58,9 @@ import java.util.LinkedHashSet;
import java.util.List;
public class KotlinInplaceVariableIntroducer<D extends JetCallableDeclaration> extends InplaceVariableIntroducer<JetExpression> {
public static final String TYPE_REFERENCE_VARIABLE_NAME = "TypeReferenceVariable";
public static final String PRIMARY_VARIABLE_NAME = "PrimaryVariable";
private static final Function0<Boolean> TRUE = new Function0<Boolean>() {
@Override
public Boolean invoke() {
@@ -329,10 +334,20 @@ public class KotlinInplaceVariableIntroducer<D extends JetCallableDeclaration> e
return nameSuggestions;
}
protected void revalidate() {
getContentPanel().revalidate();
if (myTarget != null) {
myBalloon.revalidate(new PositionTracker.Static<Balloon>(myTarget));
}
}
protected void addTypeReferenceVariable(TemplateBuilderImpl builder) {
JetTypeReference typeReference = myDeclaration.getTypeReference();
if (typeReference != null) {
builder.replaceElement(typeReference, SpecifyTypeExplicitlyAction.createTypeExpressionForTemplate(myExprType));
builder.replaceElement(typeReference,
TYPE_REFERENCE_VARIABLE_NAME,
SpecifyTypeExplicitlyAction.createTypeExpressionForTemplate(myExprType),
false);
}
}
@@ -0,0 +1,9 @@
class A(a: Int) {
init {
val t = <selection>1 + 2</selection>
}
}
fun test() {
A(1)
}
@@ -0,0 +1,9 @@
class A(a: Int, i: Int = 1 + 2) {
init {
val t = i
}
}
fun test() {
A(1)
}
@@ -0,0 +1,7 @@
class A(a: Int) {
val x = <selection>1 + 2</selection>
}
fun test() {
A(1)
}
@@ -0,0 +1,7 @@
class A(a: Int, i: Int = 1 + 2) {
val x = i
}
fun test() {
A(1)
}
@@ -0,0 +1,7 @@
class A {
val a = <selection>1 + 2</selection>
}
fun test() {
A()
}
@@ -0,0 +1,7 @@
class A(i: Int = 1 + 2) {
val a = i
}
fun test() {
A()
}
@@ -0,0 +1,8 @@
// TARGET:
class A(a: Int) {
fun foo() = <selection>1 + 2</selection>
}
fun test() {
A(1)
}
@@ -0,0 +1,8 @@
// TARGET:
class A(a: Int, val i: Int = 1 + 2) {
fun foo() = i
}
fun test() {
A(1)
}
@@ -0,0 +1,8 @@
fun foo(a: Int): Int {
val b = <selection>(a + 1)</selection> * 2
return a + b
}
fun test() {
foo(1)
}
@@ -0,0 +1,8 @@
fun foo(a: Int, i: Int = a + 1): Int {
val b = (i) * 2
return a + b
}
fun test() {
foo(1)
}
@@ -0,0 +1,5 @@
trait T
fun foo() {
val a = <selection>object: T {}</selection>
}
@@ -0,0 +1,5 @@
trait T
fun foo(t: T = object : T {}) {
val a = t
}
@@ -0,0 +1,8 @@
fun foo(a: Int): Int {
val b = (<selection>a + 1</selection>) * 2
return a + b
}
fun test() {
foo(1)
}
@@ -0,0 +1,8 @@
fun foo(a: Int, i: Int = a + 1): Int {
val b = (i) * 2
return a + b
}
fun test() {
foo(1)
}
@@ -0,0 +1,4 @@
var foo: Int = 1
set(m: Int) {
val t = <selection>m + 1</selection>
}
@@ -0,0 +1 @@
Cannot refactor in this place
@@ -0,0 +1,10 @@
class A {
constructor(a: Int) {
val b = (<selection>a + 1</selection>) * 2
val t = a + b
}
}
fun test() {
A(1)
}
@@ -0,0 +1,10 @@
class A {
constructor(a: Int, i: Int = a + 1) {
val b = (i) * 2
val t = a + b
}
}
fun test() {
A(1)
}
@@ -25,16 +25,18 @@ import com.intellij.psi.util.PsiTreeUtil
import com.intellij.refactoring.BaseRefactoringProcessor.ConflictsInTestsException
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.JetLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.PluginTestCaseBase
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.refactoring.JetRefactoringUtil
import org.jetbrains.kotlin.idea.refactoring.introduce.extractFunction.ExtractKotlinFunctionHandler
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.*
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceParameter.KotlinIntroduceParameterHandler
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceProperty.KotlinIntroducePropertyHandler
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinIntroduceVariableHandler
import org.jetbrains.kotlin.psi.JetDeclaration
import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.psi.JetPackageDirective
import org.jetbrains.kotlin.psi.JetTreeVisitorVoid
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.test.ConfigLibraryUtil
import org.jetbrains.kotlin.test.InTextDirectivesUtils
@@ -61,6 +63,22 @@ public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTe
}
}
protected fun doIntroduceParameterTest(path: String) {
doTest(path) { file ->
with (KotlinIntroduceParameterHandler()) {
val target = file.findElementByComment("// TARGET:") as? JetNamedDeclaration
if (target != null) {
JetRefactoringUtil.selectExpression(fixture.getEditor(), file, true) { expression ->
invoke(fixture.getProject(), fixture.getEditor(), expression!!, target)
}
}
else {
invoke(fixture.getProject(), fixture.getEditor(), file, null)
}
}
}
}
protected fun doIntroducePropertyTest(path: String) {
doTest(path) { file ->
val extractionTarget = propertyTargets.single {
@@ -32,6 +32,7 @@ import java.util.regex.Pattern;
JetExtractionTestGenerated.IntroduceVariable.class,
JetExtractionTestGenerated.ExtractFunction.class,
JetExtractionTestGenerated.IntroduceProperty.class,
JetExtractionTestGenerated.IntroduceParameter.class,
})
@RunWith(JUnit3RunnerWithInners.class)
public class JetExtractionTestGenerated extends AbstractJetExtractionTest {
@@ -2181,4 +2182,67 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest {
doIntroducePropertyTest(fileName);
}
}
@TestMetadata("idea/testData/refactoring/introduceParameter")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class IntroduceParameter extends AbstractJetExtractionTest {
public void testAllFilesPresentInIntroduceParameter() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/refactoring/introduceParameter"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("classInAnonymousInitializer.kt")
public void testClassInAnonymousInitializer() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/classInAnonymousInitializer.kt");
doIntroduceParameterTest(fileName);
}
@TestMetadata("classInPropertyInitializer.kt")
public void testClassInPropertyInitializer() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/classInPropertyInitializer.kt");
doIntroduceParameterTest(fileName);
}
@TestMetadata("classNoParams.kt")
public void testClassNoParams() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/classNoParams.kt");
doIntroduceParameterTest(fileName);
}
@TestMetadata("classParameterInFunctionBody.kt")
public void testClassParameterInFunctionBody() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/classParameterInFunctionBody.kt");
doIntroduceParameterTest(fileName);
}
@TestMetadata("defaultValueInParens.kt")
public void testDefaultValueInParens() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/defaultValueInParens.kt");
doIntroduceParameterTest(fileName);
}
@TestMetadata("functionWithApproximatedType.kt")
public void testFunctionWithApproximatedType() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/functionWithApproximatedType.kt");
doIntroduceParameterTest(fileName);
}
@TestMetadata("functionWithDefaultValue.kt")
public void testFunctionWithDefaultValue() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/functionWithDefaultValue.kt");
doIntroduceParameterTest(fileName);
}
@TestMetadata("propertyAccessor.kt")
public void testPropertyAccessor() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/propertyAccessor.kt");
doIntroduceParameterTest(fileName);
}
@TestMetadata("secondaryConstructorWithDefaultValue.kt")
public void testSecondaryConstructorWithDefaultValue() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/secondaryConstructorWithDefaultValue.kt");
doIntroduceParameterTest(fileName);
}
}
}