Converted to Kotlin + no i18n

This commit is contained in:
Valentin Kipyatkov
2015-10-14 20:50:21 +03:00
parent 17123a3cf2
commit 78ac41a676
3 changed files with 193 additions and 228 deletions
@@ -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}''
@@ -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<JetReferenceExpression> {
private static final String PRIMARY_USAGE = "PrimaryUsage";
private static final String OTHER_USAGE = "OtherUsage";
private final ClassDescriptor platformClass;
private final Collection<ClassDescriptor> possibleClasses;
public MapPlatformClassToKotlinFix(@NotNull JetReferenceExpression element, @NotNull ClassDescriptor platformClass,
@NotNull Collection<ClassDescriptor> 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<Diagnostic> diagnostics = context.getDiagnostics();
List<JetImportDirective> imports = new ArrayList<JetImportDirective>();
List<JetUserType> usages = new ArrayList<JetUserType>();
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<PsiElement> replacedElements = replaceUsagesWithFirstClass(project, usages);
if (possibleClasses.size() > 1) {
LinkedHashSet<String> possibleTypes = new LinkedHashSet<String>();
for (ClassDescriptor klass : possibleClasses) {
possibleTypes.add(klass.getName().asString());
}
buildAndShowTemplate(project, editor, file, replacedElements, possibleTypes);
}
}
private List<PsiElement> replaceUsagesWithFirstClass(Project project, List<JetUserType> usages) {
ClassDescriptor replacementClass = possibleClasses.iterator().next();
String replacementClassName = replacementClass.getName().asString();
List<PsiElement> replacedElements = new ArrayList<PsiElement>();
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<PsiElement> replacedElements, LinkedHashSet<String> 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<JetElement, Collection<ClassDescriptor>> 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<? extends DeclarationDescriptor> 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;
}
}
@@ -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<ClassDescriptor>
) : KotlinQuickFixAction<JetReferenceExpression>(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<JetImportDirective>()
val usages = ArrayList<JetUserType>()
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<String>()
for (klass in possibleClasses) {
possibleTypes.add(klass.name.asString())
}
buildAndShowTemplate(project, editor, file, replacedElements, possibleTypes)
}
}
private fun replaceUsagesWithFirstClass(project: Project, usages: List<JetUserType>): List<PsiElement> {
val replacementClass = possibleClasses.iterator().next()
val replacementClassName = replacementClass.name.asString()
val replacedElements = ArrayList<PsiElement>()
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<PsiElement>, options: LinkedHashSet<String>) {
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
}
}
}