From a1e9259465e8cb8ac8ed6ac906538472eb57e31e Mon Sep 17 00:00:00 2001 From: Nicolay Mitropolsky Date: Wed, 5 Jun 2019 14:38:30 +0300 Subject: [PATCH] Revert "Remove broken 192 bunch files" They are not broken anymore as long as we build with "real" IDEA 192 This reverts commit 67a194fa --- .../ChangeMethodParameters.kt.192 | 231 ++++++++++++++++++ ...ommonIntentionActionsParametersTest.kt.192 | 172 +++++++++++++ 2 files changed, 403 insertions(+) create mode 100644 idea/src/org/jetbrains/kotlin/idea/quickfix/crossLanguage/ChangeMethodParameters.kt.192 create mode 100644 idea/tests/org/jetbrains/kotlin/idea/quickfix/CommonIntentionActionsParametersTest.kt.192 diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/crossLanguage/ChangeMethodParameters.kt.192 b/idea/src/org/jetbrains/kotlin/idea/quickfix/crossLanguage/ChangeMethodParameters.kt.192 new file mode 100644 index 00000000000..06682bd5880 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/crossLanguage/ChangeMethodParameters.kt.192 @@ -0,0 +1,231 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ +package org.jetbrains.kotlin.idea.quickfix.crossLanguage + +import com.intellij.codeInsight.daemon.QuickFixBundle +import com.intellij.lang.jvm.actions.AnnotationRequest +import com.intellij.lang.jvm.actions.ChangeParametersRequest +import com.intellij.lang.jvm.actions.ExpectedParameter +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.text.StringUtil +import com.intellij.psi.JvmPsiConversionHelper +import org.jetbrains.kotlin.asJava.elements.KtLightElement +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.SourceElement +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl +import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny +import org.jetbrains.kotlin.idea.core.ShortenReferences +import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction +import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers +import org.jetbrains.kotlin.load.java.NOT_NULL_ANNOTATIONS +import org.jetbrains.kotlin.load.java.NULLABLE_ANNOTATIONS +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.types.KotlinType + +internal class ChangeMethodParameters( + target: KtNamedFunction, + val request: ChangeParametersRequest +) : KotlinQuickFixAction(target) { + + + override fun getText(): String { + + val target = element ?: return "" + + val helper = JvmPsiConversionHelper.getInstance(target.project) + + val parametersString = request.expectedParameters.joinToString(", ", "(", ")") { ep -> + val kotlinType = + ep.expectedTypes.firstOrNull()?.theType?.let { helper.convertType(it).resolveToKotlinType(target.getResolutionFacade()) } + "${ep.semanticNames.firstOrNull() ?: "parameter"}: ${kotlinType?.let { + IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_NO_ANNOTATIONS.renderType(it) + } ?: ""}" + } + + val shortenParameterString = StringUtil.shortenTextWithEllipsis(parametersString, 30, 5) + return QuickFixBundle.message("change.method.parameters.text", shortenParameterString) + } + + override fun getFamilyName(): String = QuickFixBundle.message("change.method.parameters.family") + + override fun isAvailable(project: Project, editor: Editor?, file: KtFile): Boolean = element != null && request.isValid + + private sealed class ParameterModification { + data class Keep(val ktParameter: KtParameter) : ParameterModification() + data class Remove(val ktParameter: KtParameter) : ParameterModification() + data class Add( + val name: String, + val ktType: KotlinType, + val expectedAnnotations: Collection, + val beforeAnchor: KtParameter? + ) : ParameterModification() + } + + private tailrec fun getParametersModifications( + target: KtNamedFunction, + currentParameters: List, + expectedParameters: List, + index: Int = 0, + collected: List = ArrayList(expectedParameters.size) + ): List { + + val expectedHead = expectedParameters.firstOrNull() ?: return collected + currentParameters.map { ParameterModification.Remove(it) } + + if (expectedHead is ChangeParametersRequest.ExistingParameterWrapper) { + val expectedExistingParameter = expectedHead.existingKtParameter + if (expectedExistingParameter == null) { + LOG.error("can't find the kotlinOrigin for parameter ${expectedHead.existingParameter} at index $index") + return collected + } + + val existingInTail = currentParameters.indexOf(expectedExistingParameter) + if (existingInTail == -1) { + throw IllegalArgumentException("can't find existing for parameter ${expectedHead.existingParameter} at index $index") + } + + return getParametersModifications( + target, + currentParameters.subList(existingInTail + 1, currentParameters.size), + expectedParameters.subList(1, expectedParameters.size), + index, + collected + + currentParameters.subList(0, existingInTail).map { ParameterModification.Remove(it) } + + ParameterModification.Keep(expectedExistingParameter) + ) + } + + val helper = JvmPsiConversionHelper.getInstance(target.project) + + val theType = expectedHead.expectedTypes.firstOrNull()?.theType ?: return emptyList() + val kotlinType = helper.convertType(theType).resolveToKotlinType(target.getResolutionFacade()) ?: return emptyList() + + return getParametersModifications( + target, + currentParameters, + expectedParameters.subList(1, expectedParameters.size), + index + 1, + collected + ParameterModification.Add( + expectedHead.semanticNames.firstOrNull() ?: "param$index", + kotlinType, + expectedHead.expectedAnnotations, + currentParameters.firstOrNull { anchor -> + expectedParameters.any { + it is ChangeParametersRequest.ExistingParameterWrapper && it.existingKtParameter == anchor + } + }) + ) + + } + + private val ChangeParametersRequest.ExistingParameterWrapper.existingKtParameter + get() = (existingParameter as? KtLightElement<*, *>)?.kotlinOrigin as? KtParameter + + + override fun invoke(project: Project, editor: Editor?, file: KtFile) { + if (!request.isValid) return + + val target = element ?: return + val functionDescriptor = target.resolveToDescriptorIfAny(BodyResolveMode.FULL) ?: return + + val parameterActions = getParametersModifications(target, target.valueParameters, request.expectedParameters) + + val parametersGenerated = parameterActions.filterIsInstance().let { + it zip generateParameterList(project, functionDescriptor, it).parameters + }.toMap() + + for (action in parameterActions) { + when (action) { + is ParameterModification.Add -> { + val parameter = parametersGenerated.getValue(action) + for (expectedAnnotation in action.expectedAnnotations) { + addAnnotationEntry(parameter, expectedAnnotation, null) + } + val anchor = action.beforeAnchor + if (anchor != null) { + target.valueParameterList!!.addParameterBefore(parameter, anchor) + } else { + target.valueParameterList!!.addParameter(parameter) + } + } + + is ParameterModification.Keep -> { + // Do nothing + } + + is ParameterModification.Remove -> { + target.valueParameterList!!.removeParameter(action.ktParameter) + } + } + } + + ShortenReferences.DEFAULT.process(target.valueParameterList!!) + } + + private fun generateParameterList( + project: Project, + functionDescriptor: FunctionDescriptor, + paramsToAdd: List + ): KtParameterList { + val newFunctionDescriptor = SimpleFunctionDescriptorImpl.create( + functionDescriptor.containingDeclaration, + functionDescriptor.annotations, + functionDescriptor.name, + functionDescriptor.kind, + SourceElement.NO_SOURCE + ).apply { + initialize( + functionDescriptor.extensionReceiverParameter?.copy(this), + functionDescriptor.dispatchReceiverParameter, + functionDescriptor.typeParameters, + paramsToAdd.mapIndexed { index, parameter -> + ValueParameterDescriptorImpl( + this, null, index, Annotations.EMPTY, + Name.identifier(parameter.name), + parameter.ktType, declaresDefaultValue = false, + isCrossinline = false, isNoinline = false, varargElementType = null, source = SourceElement.NO_SOURCE + ) + }, + functionDescriptor.returnType, + functionDescriptor.modality, + functionDescriptor.visibility + ) + } + + + val renderer = IdeDescriptorRenderers.SOURCE_CODE.withOptions { + defaultParameterValueRenderer = null + } + + val newFunction = KtPsiFactory(project).createFunction(renderer.render(newFunctionDescriptor)).apply { + valueParameters.forEach { param -> + param.annotationEntries.forEach { a -> + a.typeReference?.run { + val fqName = FqName(this.text) + if (fqName in (NULLABLE_ANNOTATIONS + NOT_NULL_ANNOTATIONS)) a.delete() + } + } + } + } + + return newFunction.valueParameterList!! + } + + companion object { + fun create(ktNamedFunction: KtNamedFunction, request: ChangeParametersRequest): ChangeMethodParameters? = + ChangeMethodParameters(ktNamedFunction, request) + } + + +} + +private val LOG = Logger.getInstance(ChangeMethodParameters::class.java) \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/CommonIntentionActionsParametersTest.kt.192 b/idea/tests/org/jetbrains/kotlin/idea/quickfix/CommonIntentionActionsParametersTest.kt.192 new file mode 100644 index 00000000000..1e887066ea4 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/CommonIntentionActionsParametersTest.kt.192 @@ -0,0 +1,172 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.quickfix + +import com.intellij.lang.jvm.actions.* +import com.intellij.lang.jvm.types.JvmType +import com.intellij.psi.PsiType +import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase +import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor +import org.jetbrains.uast.UMethod +import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner +import org.junit.runner.RunWith + +@RunWith(JUnit3WithIdeaConfigurationRunner::class) +class CommonIntentionActionsParametersTest : LightPlatformCodeInsightFixtureTestCase() { + + override fun getProjectDescriptor() = KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE_FULL_JDK + + override fun setUp() { + super.setUp() + myFixture.configureByText("Anno.kt", "annotation class Anno(val i: Int)") + } + + fun testSetParameters() { + myFixture.configureByText( + "foo.kt", + """ + class Foo { + fun bar() {} + } + """.trimIndent() + ) + + myFixture.launchAction( + createChangeParametersActions( + myFixture.atCaret().javaPsi, + setMethodParametersRequest( + linkedMapOf( + "i" to PsiType.INT, + "file" to PsiType.getTypeByName("java.io.File", project, myFixture.file.resolveScope) + ).entries + ) + ).findWithText("Change method parameters to '(i: Int, file: File)'") + ) + myFixture.checkResult( + """ + import java.io.File + + class Foo { + fun bar(i: Int, file: File) {} + } + """.trimIndent(), + true + ) + } + + + fun testAddParameterToTheEnd() { + myFixture.configureByText( + "foo.kt", + """ + class Foo { + fun bar(@Anno(3) a: Int) {} + } + """.trimIndent() + ) + + runParametersTransformation("Change method parameters to '(a: Int, file: File)'") { currentParameters -> + currentParameters + expectedParameter( + PsiType.getTypeByName("java.io.File", project, myFixture.file.resolveScope), "file", + listOf(annotationRequest("Anno", intAttribute("i", 8))) + ) + } + + myFixture.checkResult( + """ + import java.io.File + + class Foo { + fun bar(@Anno(3) a: Int, @Anno(i = 8) file: File) {} + } + """.trimIndent(), true + ) + } + + + fun testAddParameterToTheBeginning() { + myFixture.configureByText( + "foo.kt", + """ + class Foo { + fun bar(@Anno(3) a: Int) {} + } + """.trimIndent() + ) + + runParametersTransformation("Change method parameters to '(file: File, a: Int)'") { currentParameters -> + listOf( + expectedParameter( + PsiType.getTypeByName("java.io.File", project, myFixture.file.resolveScope), "file", + listOf(annotationRequest("Anno", intAttribute("i", 8))) + ) + ) + currentParameters + } + + myFixture.checkResult( + """ + import java.io.File + + class Foo { + fun bar(@Anno(i = 8) file: File, @Anno(3) a: Int) {} + } + """.trimIndent(), true + ) + } + + fun testReplaceInTheMiddle() { + myFixture.configureByText( + "foo.kt", + """ + class Foo { + fun bar(@Anno(1) a: Int, @Anno(2) b: Int, @Anno(3) c: Int) {} + } + """.trimIndent() + ) + + runParametersTransformation("Change method parameters to '(a: Int, file: File, c: Int)'") { currentParameters -> + ArrayList(currentParameters).apply { + this[1] = expectedParameter( + PsiType.getTypeByName("java.io.File", project, myFixture.file.resolveScope), "file", + listOf(annotationRequest("Anno", intAttribute("i", 8))) + ) + } + } + + myFixture.checkResult( + """ + import java.io.File + + class Foo { + fun bar(@Anno(1) a: Int, @Anno(i = 8) file: File, @Anno(3) c: Int) {} + } + """.trimIndent(), true + ) + } + + private fun runParametersTransformation( + actionName: String, + transformation: (List) -> List + ) { + val psiMethod = myFixture.atCaret().javaPsi + val currentParameters = psiMethod.parameters.map { ChangeParametersRequest.ExistingParameterWrapper(it) } + myFixture.launchAction( + createChangeParametersActions( + psiMethod, + SimpleChangeParametersRequest( + transformation(currentParameters) + ) + ).findWithText(actionName) + ) + } + +} + +private class SimpleChangeParametersRequest(private val list: List) : ChangeParametersRequest { + override fun getExpectedParameters(): List = list + + override fun isValid(): Boolean = true +} \ No newline at end of file