[jvm-lang] java: extract common template setup logic

This commit is contained in:
Daniil Ovchinnikov
2017-11-09 18:52:51 +03:00
parent f8a6289bae
commit d7a9d86415
3 changed files with 73 additions and 61 deletions
@@ -1,25 +1,12 @@
/*
* Copyright 2000-2017 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.
*/
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.lang.java.actions
import com.intellij.codeInsight.CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement
import com.intellij.codeInsight.daemon.QuickFixBundle.message
import com.intellij.codeInsight.daemon.impl.quickfix.CreateFromUsageBaseFix
import com.intellij.codeInsight.daemon.impl.quickfix.CreateFromUsageBaseFix.positionCursor
import com.intellij.codeInsight.daemon.impl.quickfix.CreateFromUsageUtils.*
import com.intellij.codeInsight.daemon.impl.quickfix.CreateFromUsageUtils.setupEditor
import com.intellij.codeInsight.daemon.impl.quickfix.CreateFromUsageUtils.setupMethodBody
import com.intellij.codeInsight.daemon.impl.quickfix.GuessTypeParameters
import com.intellij.codeInsight.intention.impl.BaseIntentionAction
import com.intellij.codeInsight.template.Template
@@ -29,14 +16,10 @@ import com.intellij.codeInsight.template.TemplateEditingAdapter
import com.intellij.lang.java.request.CreateMethodFromJavaUsageRequest
import com.intellij.lang.jvm.JvmModifier
import com.intellij.lang.jvm.actions.CreateMethodRequest
import com.intellij.lang.jvm.actions.ExpectedTypes
import com.intellij.openapi.command.WriteCommandAction.runWriteCommandAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Computable
import com.intellij.psi.*
import com.intellij.psi.codeStyle.CodeStyleManager
import com.intellij.psi.codeStyle.SuggestedNameInfo
import com.intellij.psi.presentation.java.ClassPresentationUtil.getNameForClass
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.PsiUtil
@@ -159,40 +142,18 @@ private class JavaMethodRenderer(
private fun setupTemplate(method: PsiMethod): TemplateBuilderImpl {
val builder = TemplateBuilderImpl(method)
val substitutor = request.targetSubstitutor.toPsiSubstitutor(project)
val guesser = GuessTypeParameters(project, factory, builder, substitutor)
setupTypeElement(guesser, method.returnTypeElement, request.returnType)
builder.setupParameters(guesser, method)
createTemplateContext(builder).run {
setupTypeElement(method.returnTypeElement, request.returnType)
setupParameters(method, request.parameters)
}
builder.setEndVariableAfter(method.body ?: method)
return builder
}
private fun setupTypeElement(guesser: GuessTypeParameters, typeElement: PsiTypeElement?, types: ExpectedTypes) {
typeElement ?: return
val expectedTypes = extractExpectedTypes(project, types).toTypedArray()
guesser.setupTypeElement(typeElement, expectedTypes, javaUsage?.context, targetClass)
}
private fun TemplateBuilder.setupParameters(guesser: GuessTypeParameters, method: PsiMethod) {
val parameters = request.parameters
if (parameters.isEmpty()) return
val codeStyleManager = CodeStyleManager.getInstance(project)!!
val parameterList = method.parameterList
val isInterface = targetClass.isInterface
//255 is the maximum number of method parameters
for (i in 0 until minOf(parameters.size, 255)) {
val parameterInfo = parameters[i]
val names = extractNames(parameterInfo.first) { "p" + i }
val dummyParameter = factory.createParameter(names.first(), PsiType.INT)
if (isInterface) {
setModifierProperty(dummyParameter, PsiModifier.FINAL, false)
}
val parameter = codeStyleManager.performActionWithFormatterDisabled(Computable {
parameterList.add(dummyParameter)
}) as PsiParameter
setupTypeElement(guesser, parameter.typeElement, parameterInfo.second)
setupParameterName(parameter, names)
}
private fun createTemplateContext(builder: TemplateBuilder): TemplateContext {
val substitutor = request.targetSubstitutor.toPsiSubstitutor(project)
val guesser = GuessTypeParameters(project, factory, builder, substitutor)
return TemplateContext(project, factory, targetClass, builder, guesser, javaUsage?.context)
}
private fun startTemplate(method: PsiMethod, template: Template) {
@@ -203,17 +164,6 @@ private class JavaMethodRenderer(
}
}
private inline fun extractNames(suggestedNames: SuggestedNameInfo?, defaultName: () -> String): Array<out String> {
val names = (suggestedNames ?: SuggestedNameInfo.NULL_INFO).names
return if (names.isEmpty()) arrayOf(defaultName()) else names
}
private fun TemplateBuilder.setupParameterName(parameter: PsiParameter, names: Array<out String>) {
val nameIdentifier = parameter.nameIdentifier ?: return
val expression = ParameterNameExpression(names)
replaceElement(nameIdentifier, expression)
}
private class MyMethodBodyListener(val project: Project, val editor: Editor, val file: PsiFile) : TemplateEditingAdapter() {
override fun templateFinished(template: Template, brokenOff: Boolean) {
@@ -14,6 +14,7 @@ import com.intellij.lang.jvm.types.JvmSubstitutor
import com.intellij.openapi.project.Project
import com.intellij.psi.*
import com.intellij.psi.PsiModifier.ModifierConstant
import com.intellij.psi.codeStyle.SuggestedNameInfo
import com.intellij.psi.impl.compiled.ClsClassImpl
@ModifierConstant
@@ -78,3 +79,8 @@ private fun ExpectedType.Kind.infoKind(): Int {
internal fun JvmSubstitutor.toPsiSubstitutor(project: Project): PsiSubstitutor {
return JvmPsiConversionHelper.getInstance(project).convertSubstitutor(this)
}
internal inline fun extractNames(suggestedNames: SuggestedNameInfo?, defaultName: () -> String): Array<out String> {
val names = (suggestedNames ?: SuggestedNameInfo.NULL_INFO).names
return if (names.isEmpty()) arrayOf(defaultName()) else names
}
@@ -0,0 +1,56 @@
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.lang.java.actions
import com.intellij.codeInsight.daemon.impl.quickfix.CreateFromUsageUtils
import com.intellij.codeInsight.daemon.impl.quickfix.GuessTypeParameters
import com.intellij.codeInsight.template.TemplateBuilder
import com.intellij.lang.jvm.actions.ExpectedParameters
import com.intellij.lang.jvm.actions.ExpectedTypes
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Computable
import com.intellij.psi.*
import com.intellij.psi.codeStyle.CodeStyleManager
import com.intellij.psi.util.PsiUtil
internal class TemplateContext(
val project: Project,
val factory: PsiElementFactory,
val targetClass: PsiClass,
val builder: TemplateBuilder,
val guesser: GuessTypeParameters,
val guesserContext: PsiElement?
)
internal fun TemplateContext.setupParameters(method: PsiMethod, parameters: ExpectedParameters) {
if (parameters.isEmpty()) return
val codeStyleManager = CodeStyleManager.getInstance(project)!!
val parameterList = method.parameterList
val isInterface = targetClass.isInterface
//255 is the maximum number of method parameters
for (i in 0 until minOf(parameters.size, 255)) {
val parameterInfo = parameters[i]
val names = extractNames(parameterInfo.first) { "p" + i }
val dummyParameter = factory.createParameter(names.first(), PsiType.INT)
if (isInterface) {
PsiUtil.setModifierProperty(dummyParameter, PsiModifier.FINAL, false)
}
val parameter = codeStyleManager.performActionWithFormatterDisabled(Computable {
parameterList.add(dummyParameter)
}) as PsiParameter
setupTypeElement(parameter.typeElement, parameterInfo.second)
setupParameterName(parameter, names)
}
}
internal fun TemplateContext.setupTypeElement(typeElement: PsiTypeElement?, types: ExpectedTypes) {
typeElement ?: return
val expectedTypes = extractExpectedTypes(project, types).toTypedArray()
guesser.setupTypeElement(typeElement, expectedTypes, guesserContext, targetClass)
}
internal fun TemplateContext.setupParameterName(parameter: PsiParameter, names: Array<out String>) {
val nameIdentifier = parameter.nameIdentifier ?: return
val expression = CreateFromUsageUtils.ParameterNameExpression(names)
builder.replaceElement(nameIdentifier, expression)
}