From 78ac41a676f0a7d7571b8f128841ec97c40909a9 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Wed, 14 Oct 2015 20:50:21 +0300 Subject: [PATCH] Converted to Kotlin + no i18n --- .../kotlin/idea/JetBundle.properties | 4 - .../quickfix/MapPlatformClassToKotlinFix.java | 224 ------------------ .../quickfix/MapPlatformClassToKotlinFix.kt | 193 +++++++++++++++ 3 files changed, 193 insertions(+), 228 deletions(-) delete mode 100644 idea/src/org/jetbrains/kotlin/idea/quickfix/MapPlatformClassToKotlinFix.java create mode 100644 idea/src/org/jetbrains/kotlin/idea/quickfix/MapPlatformClassToKotlinFix.kt diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties index 7106fd72601..2002be754b7 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties @@ -161,10 +161,6 @@ move.when.else.branch.to.the.end.action=Move else branch to the end move.when.else.branch.to.the.end.family.name=Move Else Branch to the End change.to.property.name.family.name=Change to property name change.to.property.name.action=Change ''{0}'' to ''{1}'' -map.platform.class.to.kotlin=Change all usages of ''{0}'' in this file to ''{1}'' -map.platform.class.to.kotlin.multiple=Change all usages of ''{0}'' in this file to a Kotlin class -map.platform.class.to.kotlin.advertisement=Choose an appropriate Kotlin class -map.platform.class.to.kotlin.family=Change to Kotlin class create.from.usage.family=Create from usage create.local.variable.from.usage=Create local variable ''{0}'' create.parameter.from.usage=Create parameter ''{0}'' diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/MapPlatformClassToKotlinFix.java b/idea/src/org/jetbrains/kotlin/idea/quickfix/MapPlatformClassToKotlinFix.java deleted file mode 100644 index 06fca2dc2be..00000000000 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/MapPlatformClassToKotlinFix.java +++ /dev/null @@ -1,224 +0,0 @@ -/* - * 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.quickfix; - -import com.intellij.codeInsight.intention.IntentionAction; -import com.intellij.codeInsight.template.*; -import com.intellij.openapi.editor.CaretModel; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.refactoring.rename.inplace.MyLookupExpression; -import com.intellij.util.IncorrectOperationException; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.descriptors.ClassDescriptor; -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; -import org.jetbrains.kotlin.diagnostics.Diagnostic; -import org.jetbrains.kotlin.diagnostics.DiagnosticWithParameters1; -import org.jetbrains.kotlin.diagnostics.Errors; -import org.jetbrains.kotlin.idea.JetBundle; -import org.jetbrains.kotlin.idea.caches.resolve.ResolutionUtils; -import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil; -import org.jetbrains.kotlin.psi.*; -import org.jetbrains.kotlin.renderer.DescriptorRenderer; -import org.jetbrains.kotlin.resolve.BindingContext; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.List; - -public class MapPlatformClassToKotlinFix extends KotlinQuickFixAction { - private static final String PRIMARY_USAGE = "PrimaryUsage"; - private static final String OTHER_USAGE = "OtherUsage"; - - private final ClassDescriptor platformClass; - private final Collection possibleClasses; - - public MapPlatformClassToKotlinFix(@NotNull JetReferenceExpression element, @NotNull ClassDescriptor platformClass, - @NotNull Collection possibleClasses) { - super(element); - this.platformClass = platformClass; - this.possibleClasses = possibleClasses; - } - - @NotNull - @Override - public String getText() { - String platformClassQualifiedName = DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(platformClass.getDefaultType()); - return possibleClasses.size() == 1 - ? JetBundle.message("map.platform.class.to.kotlin", platformClassQualifiedName, - DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(possibleClasses.iterator().next().getDefaultType())) - : JetBundle.message("map.platform.class.to.kotlin.multiple", platformClassQualifiedName); - } - - @NotNull - @Override - public String getFamilyName() { - return JetBundle.message("map.platform.class.to.kotlin.family"); - } - - @Override - public void invoke(@NotNull Project project, Editor editor, JetFile file) throws IncorrectOperationException { - BindingContext context = ResolutionUtils.analyzeFully(file); - Iterable diagnostics = context.getDiagnostics(); - List imports = new ArrayList(); - List usages = new ArrayList(); - - for (Diagnostic diagnostic : diagnostics) { - if (diagnostic.getFactory() != Errors.PLATFORM_CLASS_MAPPED_TO_KOTLIN) continue; - JetReferenceExpression refExpr = getImportOrUsageFromDiagnostic(diagnostic); - if (refExpr == null) continue; - ClassDescriptor descriptor = resolveToClass(refExpr, context); - if (descriptor == null || !(descriptor.equals(platformClass))) continue; - JetImportDirective imp = PsiTreeUtil.getParentOfType(refExpr, JetImportDirective.class); - if (imp == null) { - JetUserType type = PsiTreeUtil.getParentOfType(refExpr, JetUserType.class); - if (type == null) continue; - usages.add(type); - } - else { - imports.add(imp); - } - } - - for (JetImportDirective imp : imports) { - imp.delete(); - } - - if (usages.isEmpty()) { // if we are not going to replace any usages, there's no reason to continue at all - return; - } - - List replacedElements = replaceUsagesWithFirstClass(project, usages); - - if (possibleClasses.size() > 1) { - LinkedHashSet possibleTypes = new LinkedHashSet(); - for (ClassDescriptor klass : possibleClasses) { - possibleTypes.add(klass.getName().asString()); - } - buildAndShowTemplate(project, editor, file, replacedElements, possibleTypes); - } - } - - private List replaceUsagesWithFirstClass(Project project, List usages) { - ClassDescriptor replacementClass = possibleClasses.iterator().next(); - String replacementClassName = replacementClass.getName().asString(); - List replacedElements = new ArrayList(); - for (JetUserType usage : usages) { - JetTypeArgumentList typeArguments = usage.getTypeArgumentList(); - String typeArgumentsString = typeArguments == null ? "" : typeArguments.getText(); - JetTypeReference replacementType = JetPsiFactoryKt.JetPsiFactory(project).createType(replacementClassName + typeArgumentsString); - JetTypeElement replacementTypeElement = replacementType.getTypeElement(); - assert replacementTypeElement != null; - PsiElement replacedElement = usage.replace(replacementTypeElement); - PsiElement replacedExpression = replacedElement.getFirstChild(); - assert replacedExpression instanceof JetSimpleNameExpression; // assumption: the Kotlin class requires no imports - replacedElements.add(replacedExpression); - } - return replacedElements; - } - - private static void buildAndShowTemplate( - Project project, Editor editor, PsiFile file, - Collection replacedElements, LinkedHashSet options - ) { - PsiDocumentManager.getInstance(project).commitAllDocuments(); - PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.getDocument()); - - PsiElement primaryReplacedExpression = replacedElements.iterator().next(); - - final CaretModel caretModel = editor.getCaretModel(); - final int oldOffset = caretModel.getOffset(); - caretModel.moveToOffset(file.getNode().getStartOffset()); - - TemplateBuilderImpl builder = new TemplateBuilderImpl(file); - Expression expression = new MyLookupExpression(primaryReplacedExpression.getText(), options, null, null, false, - JetBundle.message("map.platform.class.to.kotlin.advertisement")); - - builder.replaceElement(primaryReplacedExpression, PRIMARY_USAGE, expression, true); - for (PsiElement replacedExpression : replacedElements) { - if (replacedExpression == primaryReplacedExpression) continue; - builder.replaceElement(replacedExpression, OTHER_USAGE, PRIMARY_USAGE, false); - } - TemplateManager.getInstance(project).startTemplate(editor, builder.buildInlineTemplate(), new TemplateEditingAdapter() { - @Override - public void templateFinished(Template template, boolean brokenOff) { - caretModel.moveToOffset(oldOffset); - } - }); - } - - private static JetReferenceExpression getImportOrUsageFromDiagnostic(@NotNull Diagnostic diagnostic) { - JetImportDirective imp = QuickFixUtil.getParentElementOfType(diagnostic, JetImportDirective.class); - JetReferenceExpression typeExpr; - if (imp == null) { - JetUserType type = QuickFixUtil.getParentElementOfType(diagnostic, JetUserType.class); - if (type == null) return null; - typeExpr = type.getReferenceExpression(); - } else { - JetExpression importRef = imp.getImportedReference(); - if (importRef == null || !(importRef instanceof JetDotQualifiedExpression)) return null; - JetExpression refExpr = ((JetDotQualifiedExpression) importRef).getSelectorExpression(); - if (refExpr == null || !(refExpr instanceof JetReferenceExpression)) return null; - typeExpr = (JetReferenceExpression) refExpr; - } - return typeExpr; - } - - public static JetSingleIntentionActionFactory createFactory() { - return new JetSingleIntentionActionFactory() { - @Nullable - @Override - public IntentionAction createAction(Diagnostic diagnostic) { - JetReferenceExpression typeExpr = getImportOrUsageFromDiagnostic(diagnostic); - if (typeExpr == null) return null; - - BindingContext context = ResolutionUtils.analyze(typeExpr); - ClassDescriptor platformClass = resolveToClass(typeExpr, context); - if (platformClass == null) return null; - - DiagnosticWithParameters1> parametrizedDiagnostic = - Errors.PLATFORM_CLASS_MAPPED_TO_KOTLIN.cast(diagnostic); - - return new MapPlatformClassToKotlinFix(typeExpr, platformClass, parametrizedDiagnostic.getA()); - } - }; - } - - @Nullable - private static ClassDescriptor resolveToClass(@NotNull JetReferenceExpression referenceExpression, @NotNull BindingContext context) { - DeclarationDescriptor descriptor = context.get(BindingContext.REFERENCE_TARGET, referenceExpression); - Collection ambiguousTargets = - context.get(BindingContext.AMBIGUOUS_REFERENCE_TARGET, referenceExpression); - if (descriptor instanceof ClassDescriptor) { - return (ClassDescriptor) descriptor; - } - else if (ambiguousTargets != null) { - for (DeclarationDescriptor target : ambiguousTargets) { - if (target instanceof ClassDescriptor) { - return (ClassDescriptor) target; - } - } - } - return null; - } -} diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/MapPlatformClassToKotlinFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/MapPlatformClassToKotlinFix.kt new file mode 100644 index 00000000000..af5d0670973 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/MapPlatformClassToKotlinFix.kt @@ -0,0 +1,193 @@ +/* + * 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.quickfix + +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.codeInsight.template.Template +import com.intellij.codeInsight.template.TemplateBuilderImpl +import com.intellij.codeInsight.template.TemplateEditingAdapter +import com.intellij.codeInsight.template.TemplateManager +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.util.PsiTreeUtil +import com.intellij.refactoring.rename.inplace.MyLookupExpression +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully +import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.renderer.DescriptorRenderer +import org.jetbrains.kotlin.resolve.BindingContext +import java.util.* + +class MapPlatformClassToKotlinFix( + element: JetReferenceExpression, + private val platformClass: ClassDescriptor, + private val possibleClasses: Collection +) : KotlinQuickFixAction(element) { + + override fun getText(): String { + val platformClassQualifiedName = DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(platformClass.defaultType) + val singleClass = possibleClasses.singleOrNull() + return if (singleClass != null) + "Change all usages of '$platformClassQualifiedName' in this file to '${DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(singleClass.defaultType)}'" + else + "Change all usages of '$platformClassQualifiedName' in this file to a Kotlin class" + } + + override fun getFamilyName() = "Change to Kotlin class" + + public override fun invoke(project: Project, editor: Editor?, file: JetFile) { + val context = file.analyzeFully() + val diagnostics = context.diagnostics + val imports = ArrayList() + val usages = ArrayList() + + for (diagnostic in diagnostics) { + if (diagnostic.factory !== Errors.PLATFORM_CLASS_MAPPED_TO_KOTLIN) continue + val refExpr = getImportOrUsageFromDiagnostic(diagnostic) ?: continue + val descriptor = resolveToClass(refExpr, context) + if (descriptor == null || descriptor != platformClass) continue + val imp = PsiTreeUtil.getParentOfType(refExpr, JetImportDirective::class.java) + if (imp == null) { + val type = PsiTreeUtil.getParentOfType(refExpr, JetUserType::class.java) ?: continue + usages.add(type) + } else { + imports.add(imp) + } + } + + for (imp in imports) { + imp.delete() + } + + if (usages.isEmpty()) { + // if we are not going to replace any usages, there's no reason to continue at all + return + } + + val replacedElements = replaceUsagesWithFirstClass(project, usages) + + if (possibleClasses.size > 1 && editor != null) { + val possibleTypes = LinkedHashSet() + for (klass in possibleClasses) { + possibleTypes.add(klass.name.asString()) + } + buildAndShowTemplate(project, editor, file, replacedElements, possibleTypes) + } + } + + private fun replaceUsagesWithFirstClass(project: Project, usages: List): List { + val replacementClass = possibleClasses.iterator().next() + val replacementClassName = replacementClass.name.asString() + val replacedElements = ArrayList() + for (usage in usages) { + val typeArguments = usage.typeArgumentList + val typeArgumentsString = if (typeArguments == null) "" else typeArguments.text + val replacementType = JetPsiFactory(project).createType(replacementClassName + typeArgumentsString) + val replacementTypeElement = replacementType.typeElement!! + val replacedElement = usage.replace(replacementTypeElement) + val replacedExpression = replacedElement.firstChild + assert(replacedExpression is JetSimpleNameExpression) // assumption: the Kotlin class requires no imports + replacedElements.add(replacedExpression) + } + return replacedElements + } + + companion object { + private val PRIMARY_USAGE = "PrimaryUsage" + private val OTHER_USAGE = "OtherUsage" + + private fun buildAndShowTemplate( + project: Project, editor: Editor, file: PsiFile, + replacedElements: Collection, options: LinkedHashSet) { + PsiDocumentManager.getInstance(project).commitAllDocuments() + PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.document) + + val primaryReplacedExpression = replacedElements.iterator().next() + + val caretModel = editor.caretModel + val oldOffset = caretModel.offset + caretModel.moveToOffset(file.node.startOffset) + + val builder = TemplateBuilderImpl(file) + val expression = MyLookupExpression(primaryReplacedExpression.text, options, null, null, false, "Choose an appropriate Kotlin class") + + builder.replaceElement(primaryReplacedExpression, PRIMARY_USAGE, expression, true) + for (replacedExpression in replacedElements) { + if (replacedExpression === primaryReplacedExpression) continue + builder.replaceElement(replacedExpression, OTHER_USAGE, PRIMARY_USAGE, false) + } + TemplateManager.getInstance(project).startTemplate(editor, builder.buildInlineTemplate(), object : TemplateEditingAdapter() { + override fun templateFinished(template: Template?, brokenOff: Boolean) { + caretModel.moveToOffset(oldOffset) + } + }) + } + + private fun getImportOrUsageFromDiagnostic(diagnostic: Diagnostic): JetReferenceExpression? { + val imp = QuickFixUtil.getParentElementOfType(diagnostic, JetImportDirective::class.java) + val typeExpr: JetReferenceExpression? + if (imp == null) { + val type = QuickFixUtil.getParentElementOfType(diagnostic, JetUserType::class.java) ?: return null + typeExpr = type.referenceExpression + } else { + val importRef = imp.importedReference + if (importRef == null || importRef !is JetDotQualifiedExpression) return null + val refExpr = (importRef as JetDotQualifiedExpression?)?.getSelectorExpression() + if (refExpr == null || refExpr !is JetReferenceExpression) return null + typeExpr = refExpr + } + return typeExpr + } + + fun createFactory(): JetSingleIntentionActionFactory { + return object : JetSingleIntentionActionFactory() { + public override fun createAction(diagnostic: Diagnostic): IntentionAction? { + val typeExpr = getImportOrUsageFromDiagnostic(diagnostic) ?: return null + + val context = typeExpr.analyze() + val platformClass = resolveToClass(typeExpr, context) ?: return null + + val parametrizedDiagnostic = Errors.PLATFORM_CLASS_MAPPED_TO_KOTLIN.cast(diagnostic) + + return MapPlatformClassToKotlinFix(typeExpr, platformClass, parametrizedDiagnostic.a) + } + } + } + + private fun resolveToClass(referenceExpression: JetReferenceExpression, context: BindingContext): ClassDescriptor? { + val descriptor = context.get(BindingContext.REFERENCE_TARGET, referenceExpression) + val ambiguousTargets = context.get(BindingContext.AMBIGUOUS_REFERENCE_TARGET, referenceExpression) + if (descriptor is ClassDescriptor) { + return descriptor + } else if (ambiguousTargets != null) { + for (target in ambiguousTargets) { + if (target is ClassDescriptor) { + return target + } + } + } + return null + } + } +}