Add Star Projections Quick-Fix: Convert to Kotlin & refactor

This commit is contained in:
Alexey Sedunov
2015-12-25 15:55:06 +03:00
parent 2e25d2d885
commit 05506a24d7
3 changed files with 50 additions and 102 deletions
@@ -9,7 +9,6 @@ add.init.keyword.in.whole.project.family=Add 'init' keyword in whole project
insert.delegation.call=Insert ''{0}()'' call
make.class.annotation.class=Make ''{0}'' an annotation class
make.class.annotation.class.family=Make class an annotation class
add.star.projections=Add ''{0}''
change.to.star.projection=Change type arguments to {0}
change.to.star.projection.family=Change to star projection
remove.parts.from.property=Remove {0} from property
@@ -14,114 +14,63 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.quickfix;
package org.jetbrains.kotlin.idea.quickfix
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.diagnostics.Diagnostic;
import org.jetbrains.kotlin.diagnostics.DiagnosticWithParameters1;
import org.jetbrains.kotlin.diagnostics.DiagnosticWithParameters2;
import org.jetbrains.kotlin.diagnostics.Errors;
import org.jetbrains.kotlin.idea.KotlinBundle;
import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.types.expressions.TypeReconstructionUtil;
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.types.expressions.TypeReconstructionUtil
import org.jetbrains.kotlin.utils.sure
public abstract class AddStarProjectionsFix extends KotlinQuickFixAction<KtUserType> {
open class AddStarProjectionsFix private constructor(element: KtUserType,
private val argumentCount: Int) : KotlinQuickFixAction<KtUserType>(element) {
override fun getFamilyName() = "Add star projections"
override fun getText() = "Add '${TypeReconstructionUtil.getTypeNameAndStarProjectionsString("", argumentCount)}'"
private final int argumentCount;
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
assert(element.typeArguments.isEmpty())
private AddStarProjectionsFix(@NotNull KtUserType element, int count) {
super(element);
argumentCount = count;
val typeString = TypeReconstructionUtil.getTypeNameAndStarProjectionsString(element.text, argumentCount)
val replacement = KtPsiFactory(file).createType(typeString).typeElement.sure { "No type element after parsing " + typeString }
element.replace(replacement)
}
@NotNull
@Override
public String getText() {
return KotlinBundle.message("add.star.projections", TypeReconstructionUtil.getTypeNameAndStarProjectionsString("", argumentCount));
object IsExpressionFactory : KotlinSingleIntentionActionFactory() {
public override fun createAction(diagnostic: Diagnostic): IntentionAction? {
val diagnosticWithParameters = Errors.NO_TYPE_ARGUMENTS_ON_RHS.cast(diagnostic)
val typeElement: KtTypeElement = diagnosticWithParameters.psiElement.typeElement ?: return null
val unwrappedType = sequence(typeElement) { (it as? KtNullableType)?.innerType }.lastOrNull() as? KtUserType ?: return null
return AddStarProjectionsFix(unwrappedType, diagnosticWithParameters.a)
}
}
@NotNull
@Override
public String getFamilyName() {
return "Add star projections";
}
@Override
public void invoke(@NotNull Project project, Editor editor, @NotNull KtFile file) throws IncorrectOperationException {
assert getElement().getTypeArguments().isEmpty();
String typeString = TypeReconstructionUtil.getTypeNameAndStarProjectionsString(getElement().getText(), argumentCount);
KtTypeElement replacement = KtPsiFactoryKt.KtPsiFactory(file).createType(typeString).getTypeElement();
assert replacement != null : "No type element after parsing " + typeString;
getElement().replace(replacement);
}
@Override
public boolean startInWriteAction() {
return true;
}
public static KotlinSingleIntentionActionFactory createFactoryForIsExpression() {
return new KotlinSingleIntentionActionFactory() {
@Override
public IntentionAction createAction(@NotNull Diagnostic diagnostic) {
DiagnosticWithParameters2<KtTypeReference, Integer, String> diagnosticWithParameters =
Errors.NO_TYPE_ARGUMENTS_ON_RHS.cast(diagnostic);
KtTypeElement typeElement = diagnosticWithParameters.getPsiElement().getTypeElement();
while (typeElement instanceof KtNullableType) {
typeElement = ((KtNullableType) typeElement).getInnerType();
object JavaClassFactory : KotlinSingleIntentionActionFactory() {
public override fun createAction(diagnostic: Diagnostic): IntentionAction? {
val diagnosticWithParameters = Errors.WRONG_NUMBER_OF_TYPE_ARGUMENTS.cast(diagnostic)
val size = diagnosticWithParameters.a
val userType = QuickFixUtil.getParentElementOfType(diagnostic, KtUserType::class.java) ?: return null
return object : AddStarProjectionsFix(userType, size) {
override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean {
// We are looking for the occurrence of Type in javaClass<Type>()
return super.isAvailable(project, editor, file) && isZeroTypeArguments && isInsideJavaClassCall
}
if (!(typeElement instanceof KtUserType)) return null;
Integer size = diagnosticWithParameters.getA();
return new AddStarProjectionsFix((KtUserType) typeElement, size) {};
private val isZeroTypeArguments: Boolean
get() = element.typeArguments.isEmpty()
// Resolve is expensive so we use a heuristic here: the case is rare enough not to be annoying
private val isInsideJavaClassCall: Boolean
get() {
val call = element.parent.parent.parent.parent as? KtCallExpression
val callee = call?.calleeExpression as? KtSimpleNameExpression
return callee?.getReferencedName() == "javaClass"
}
}
};
}
public static KotlinSingleIntentionActionFactory createFactoryForJavaClass() {
return new KotlinSingleIntentionActionFactory() {
@Override
public IntentionAction createAction(@NotNull Diagnostic diagnostic) {
DiagnosticWithParameters1<KtElement, Integer> diagnosticWithParameters = Errors.WRONG_NUMBER_OF_TYPE_ARGUMENTS.cast(diagnostic);
Integer size = diagnosticWithParameters.getA();
KtUserType userType = QuickFixUtil.getParentElementOfType(diagnostic, KtUserType.class);
if (userType == null) return null;
return new AddStarProjectionsFix(userType, size) {
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiFile file) {
// We are looking for the occurrence of Type in javaClass<Type>()
return super.isAvailable(project, editor, file) && isZeroTypeArguments() && isInsideJavaClassCall();
}
private boolean isZeroTypeArguments() {
return getElement().getTypeArguments().isEmpty();
}
private boolean isInsideJavaClassCall() {
PsiElement parent = getElement().getParent().getParent().getParent().getParent();
if (parent instanceof KtCallExpression) {
KtExpression calleeExpression = ((KtCallExpression) parent).getCalleeExpression();
if (calleeExpression instanceof KtSimpleNameExpression) {
KtSimpleNameExpression simpleNameExpression = (KtSimpleNameExpression) calleeExpression;
// Resolve is expensive so we use a heuristic here: the case is rare enough not to be annoying
return "javaClass".equals(simpleNameExpression.getReferencedName());
}
}
return false;
}
};
}
};
}
}
}
@@ -190,8 +190,8 @@ class QuickFixRegistrar : QuickFixContributor {
NO_ELSE_IN_WHEN.registerFactory(AddWhenRemainingBranchesFix)
BREAK_OR_CONTINUE_IN_WHEN.registerFactory(AddLoopLabelFix)
NO_TYPE_ARGUMENTS_ON_RHS.registerFactory(AddStarProjectionsFix.createFactoryForIsExpression())
WRONG_NUMBER_OF_TYPE_ARGUMENTS.registerFactory(AddStarProjectionsFix.createFactoryForJavaClass())
NO_TYPE_ARGUMENTS_ON_RHS.registerFactory(AddStarProjectionsFix.IsExpressionFactory)
WRONG_NUMBER_OF_TYPE_ARGUMENTS.registerFactory(AddStarProjectionsFix.JavaClassFactory)
TYPE_ARGUMENTS_REDUNDANT_IN_SUPER_QUALIFIER.registerFactory(RemovePsiElementSimpleFix.createRemoveTypeArgumentsFactory())