Converted to Kotlin (step 1)

This commit is contained in:
Valentin Kipyatkov
2015-05-05 14:37:00 +03:00
parent 7499c4bc19
commit 1251663099
4 changed files with 136 additions and 165 deletions
@@ -14,205 +14,178 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.intentions; package org.jetbrains.kotlin.idea.intentions
import com.google.common.collect.Lists; import com.google.common.collect.Lists
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction; import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction
import com.intellij.codeInsight.template.*; import com.intellij.codeInsight.template.*
import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project
import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.descriptors.CallableDescriptor; import org.jetbrains.kotlin.idea.JetBundle
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor; import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
import org.jetbrains.kotlin.diagnostics.Diagnostic; import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.diagnostics.Errors; import org.jetbrains.kotlin.idea.util.ShortenReferences
import org.jetbrains.kotlin.idea.JetBundle; import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage; import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers; import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.idea.util.ShortenReferences; import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilPackage; import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils; import java.util.ArrayList
import org.jetbrains.kotlin.types.ErrorUtils;
import org.jetbrains.kotlin.types.JetType;
import org.jetbrains.kotlin.types.TypeUtils;
import java.util.ArrayList; public class SpecifyTypeExplicitlyAction : PsiElementBaseIntentionAction() {
import java.util.List; override fun getFamilyName(): String {
import java.util.Set; return JetBundle.message("specify.type.explicitly.action.family.name")
import static org.jetbrains.kotlin.psi.PsiPackage.JetPsiFactory;
public class SpecifyTypeExplicitlyAction extends PsiElementBaseIntentionAction {
@NotNull
@Override
public String getFamilyName() {
return JetBundle.message("specify.type.explicitly.action.family.name");
} }
@Override override fun invoke(project: Project, editor: Editor, element: PsiElement) {
public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull PsiElement element) { var element = element
JetTypeReference typeRefParent = PsiTreeUtil.getTopmostParentOfType(element, JetTypeReference.class); val typeRefParent = PsiTreeUtil.getTopmostParentOfType<JetTypeReference>(element, javaClass<JetTypeReference>())
if (typeRefParent != null) { if (typeRefParent != null) {
element = typeRefParent; element = typeRefParent
} }
JetCallableDeclaration declaration = (JetCallableDeclaration)element.getParent(); val declaration = element.getParent() as JetCallableDeclaration
JetType type = getTypeForDeclaration(declaration); val type = getTypeForDeclaration(declaration)
if (declaration.getTypeReference() == null) { if (declaration.getTypeReference() == null) {
addTypeAnnotation(project, editor, declaration, type); addTypeAnnotation(project, editor, declaration, type)
} }
else { else {
declaration.setTypeReference(null); declaration.setTypeReference(null)
} }
} }
@Override override fun isAvailable(project: Project, editor: Editor, element: PsiElement): Boolean {
public boolean isAvailable(@NotNull Project project, @NotNull Editor editor, @NotNull PsiElement element) { var element = element
if (element.getContainingFile() instanceof JetCodeFragment) { if (element.getContainingFile() is JetCodeFragment) {
return false; return false
} }
JetTypeReference typeRefParent = PsiTreeUtil.getTopmostParentOfType(element, JetTypeReference.class); val typeRefParent = PsiTreeUtil.getTopmostParentOfType<JetTypeReference>(element, javaClass<JetTypeReference>())
if (typeRefParent != null) { if (typeRefParent != null) {
element = typeRefParent; element = typeRefParent
} }
PsiElement parent = element.getParent(); val parent = element.getParent()
if (!(parent instanceof JetCallableDeclaration)) return false; if (parent !is JetCallableDeclaration) return false
JetCallableDeclaration declaration = (JetCallableDeclaration) parent;
if (declaration instanceof JetProperty && !PsiTreeUtil.isAncestor(((JetProperty) declaration).getInitializer(), element, false)) { if (parent is JetProperty && !PsiTreeUtil.isAncestor(parent.getInitializer(), element, false)) {
if (declaration.getTypeReference() != null) { if (parent.getTypeReference() != null) {
setText(JetBundle.message("specify.type.explicitly.remove.action.name")); setText(JetBundle.message("specify.type.explicitly.remove.action.name"))
return true; return true
} }
else { else {
setText(JetBundle.message("specify.type.explicitly.add.action.name")); setText(JetBundle.message("specify.type.explicitly.add.action.name"))
} }
} }
else if (declaration instanceof JetNamedFunction && declaration.getTypeReference() == null else if (parent is JetNamedFunction && parent.getTypeReference() == null && !parent.hasBlockBody()) {
&& !((JetNamedFunction) declaration).hasBlockBody()) { setText(JetBundle.message("specify.type.explicitly.add.return.type.action.name"))
setText(JetBundle.message("specify.type.explicitly.add.return.type.action.name"));
} }
else if (declaration instanceof JetParameter && ((JetParameter) declaration).isLoopParameter()) { else if (parent is JetParameter && parent.isLoopParameter()) {
if (declaration.getTypeReference() != null) { if (parent.getTypeReference() != null) {
setText(JetBundle.message("specify.type.explicitly.remove.action.name")); setText(JetBundle.message("specify.type.explicitly.remove.action.name"))
return true; return true
} }
else { else {
setText(JetBundle.message("specify.type.explicitly.add.action.name")); setText(JetBundle.message("specify.type.explicitly.add.action.name"))
} }
} }
else { else {
return false; return false
} }
if (getTypeForDeclaration(declaration).isError()) { if (getTypeForDeclaration(parent).isError()) {
return false; return false
} }
return !hasPublicMemberDiagnostic(declaration); return !hasPublicMemberDiagnostic(parent)
} }
companion object {
private static boolean hasPublicMemberDiagnostic(@NotNull JetNamedDeclaration declaration) {
BindingContext bindingContext = ResolvePackage.analyzeFully(declaration.getContainingJetFile());
for (Diagnostic diagnostic : bindingContext.getDiagnostics()) {
//noinspection ConstantConditions
if (Errors.PUBLIC_MEMBER_SHOULD_SPECIFY_TYPE == diagnostic.getFactory() && declaration == diagnostic.getPsiElement()) {
return true;
}
}
return false;
}
@NotNull private fun hasPublicMemberDiagnostic(declaration: JetNamedDeclaration): Boolean {
public static JetType getTypeForDeclaration(@NotNull JetCallableDeclaration declaration) { val bindingContext = declaration.getContainingJetFile().analyzeFully()
BindingContext bindingContext = ResolvePackage.analyzeFully(declaration.getContainingJetFile()); for (diagnostic in bindingContext.getDiagnostics()) {
CallableDescriptor descriptor = (CallableDescriptor) bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration); //noinspection ConstantConditions
if (Errors.PUBLIC_MEMBER_SHOULD_SPECIFY_TYPE == diagnostic.getFactory() && declaration == diagnostic.getPsiElement()) {
JetType type = descriptor != null ? descriptor.getReturnType() : null; return true
return type == null ? ErrorUtils.createErrorType("null type") : type;
}
@NotNull
public static Expression createTypeExpressionForTemplate(JetType exprType) {
ClassifierDescriptor descriptor = exprType.getConstructor().getDeclarationDescriptor();
boolean isAnonymous = descriptor != null && DescriptorUtils.isAnonymousObject(descriptor);
Set<JetType> allSupertypes = TypeUtils.getAllSupertypes(exprType);
List<JetType> types = isAnonymous ? new ArrayList<JetType>() : Lists.newArrayList(exprType);
types.addAll(allSupertypes);
return new JetTypeLookupExpression<JetType>(
types,
types.iterator().next(),
JetBundle.message("specify.type.explicitly.add.action.name")
) {
@Override
protected String getLookupString(JetType element) {
return IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(element);
}
@Override
protected String getResult(JetType element) {
return IdeDescriptorRenderers.SOURCE_CODE.renderType(element);
}
};
}
public static void addTypeAnnotation(Project project, @Nullable Editor editor, @NotNull JetCallableDeclaration declaration, @NotNull JetType exprType) {
if (editor != null) {
addTypeAnnotationWithTemplate(project, editor, declaration, exprType);
}
else {
declaration.setTypeReference(anyTypeRef(project));
}
}
public static TemplateEditingAdapter createTypeReferencePostprocessor(final JetCallableDeclaration declaration) {
return new TemplateEditingAdapter() {
@Override
public void templateFinished(Template template, boolean brokenOff) {
JetTypeReference typeRef = declaration.getTypeReference();
if (typeRef != null && typeRef.isValid()) {
ShortenReferences.DEFAULT.process(typeRef);
} }
} }
}; return false
} }
private static void addTypeAnnotationWithTemplate( public fun getTypeForDeclaration(declaration: JetCallableDeclaration): JetType {
@NotNull Project project, val bindingContext = declaration.getContainingJetFile().analyzeFully()
@NotNull Editor editor,
@NotNull JetCallableDeclaration declaration,
@NotNull JetType exprType
) {
assert !exprType.isError() : "Unexpected error type, should have been checked before: "
+ PsiUtilPackage.getElementTextWithContext(declaration) + ", type = " + exprType;
Expression expression = createTypeExpressionForTemplate(exprType); val type = if (bindingContext.get<PsiElement, DeclarationDescriptor>(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration) != null) bindingContext.get<PsiElement, DeclarationDescriptor>(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration).getReturnType() else null
return type ?: ErrorUtils.createErrorType("null type")
}
declaration.setTypeReference(anyTypeRef(project)); public fun createTypeExpressionForTemplate(exprType: JetType): Expression {
val descriptor = exprType.getConstructor().getDeclarationDescriptor()
val isAnonymous = descriptor != null && DescriptorUtils.isAnonymousObject(descriptor)
PsiDocumentManager.getInstance(project).commitAllDocuments(); val allSupertypes = TypeUtils.getAllSupertypes(exprType)
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.getDocument()); val types = if (isAnonymous) ArrayList<JetType>() else Lists.newArrayList<JetType>(exprType)
types.addAll(allSupertypes)
JetTypeReference newTypeRef = declaration.getTypeReference(); return object : JetTypeLookupExpression<JetType>(types, types.iterator().next(), JetBundle.message("specify.type.explicitly.add.action.name")) {
assert newTypeRef != null; override fun getLookupString(element: JetType): String {
TemplateBuilderImpl builder = new TemplateBuilderImpl(newTypeRef); return IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(element)
builder.replaceElement(newTypeRef, expression); }
editor.getCaretModel().moveToOffset(newTypeRef.getNode().getStartOffset()); override fun getResult(element: JetType): String {
return IdeDescriptorRenderers.SOURCE_CODE.renderType(element)
}
}
}
TemplateManager.getInstance(project) public fun addTypeAnnotation(project: Project, editor: Editor?, declaration: JetCallableDeclaration, exprType: JetType) {
.startTemplate(editor, builder.buildInlineTemplate(), createTypeReferencePostprocessor(declaration)); if (editor != null) {
} addTypeAnnotationWithTemplate(project, editor, declaration, exprType)
}
else {
declaration.setTypeReference(anyTypeRef(project))
}
}
private static JetTypeReference anyTypeRef(@NotNull Project project) { public fun createTypeReferencePostprocessor(declaration: JetCallableDeclaration): TemplateEditingAdapter {
return JetPsiFactory(project).createType("Any"); return object : TemplateEditingAdapter() {
override fun templateFinished(template: Template?, brokenOff: Boolean) {
val typeRef = declaration.getTypeReference()
if (typeRef != null && typeRef.isValid()) {
ShortenReferences.DEFAULT.process(typeRef)
}
}
}
}
private fun addTypeAnnotationWithTemplate(project: Project, editor: Editor, declaration: JetCallableDeclaration, exprType: JetType) {
assert(!exprType.isError()) { "Unexpected error type, should have been checked before: " + declaration.getElementTextWithContext() + ", type = " + exprType }
val expression = createTypeExpressionForTemplate(exprType)
declaration.setTypeReference(anyTypeRef(project))
PsiDocumentManager.getInstance(project).commitAllDocuments()
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.getDocument())
val newTypeRef = declaration.getTypeReference()
assert(newTypeRef != null)
val builder = TemplateBuilderImpl(newTypeRef)
builder.replaceElement(newTypeRef, expression)
editor.getCaretModel().moveToOffset(newTypeRef!!.getNode().getStartOffset())
TemplateManager.getInstance(project).startTemplate(editor, builder.buildInlineTemplate(), createTypeReferencePostprocessor(declaration))
}
private fun anyTypeRef(project: Project): JetTypeReference {
return JetPsiFactory(project).createType("Any")
}
} }
} }
@@ -116,7 +116,7 @@ public class RemovePartsFromPropertyFix extends JetIntentionAction<JetProperty>
} }
element = (JetProperty) element.replace(newElement); element = (JetProperty) element.replace(newElement);
if (typeToAdd != null) { if (typeToAdd != null) {
SpecifyTypeExplicitlyAction.addTypeAnnotation(project, editor, element, typeToAdd); SpecifyTypeExplicitlyAction.Companion.addTypeAnnotation(project, editor, element, typeToAdd);
} }
} }
@@ -23,14 +23,12 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.idea.JetBundle; import org.jetbrains.kotlin.idea.JetBundle;
import org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyAction;
import org.jetbrains.kotlin.psi.JetCallableDeclaration; import org.jetbrains.kotlin.psi.JetCallableDeclaration;
import org.jetbrains.kotlin.psi.JetNamedFunction; import org.jetbrains.kotlin.psi.JetNamedFunction;
import org.jetbrains.kotlin.psi.JetProperty; import org.jetbrains.kotlin.psi.JetProperty;
import org.jetbrains.kotlin.types.JetType; import org.jetbrains.kotlin.types.JetType;
import static org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyAction.addTypeAnnotation;
import static org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyAction.getTypeForDeclaration;
@SuppressWarnings("IntentionDescriptionNotFoundInspection") @SuppressWarnings("IntentionDescriptionNotFoundInspection")
public class SpecifyTypeExplicitlyFix extends PsiElementBaseIntentionAction { public class SpecifyTypeExplicitlyFix extends PsiElementBaseIntentionAction {
@NotNull @NotNull
@@ -43,8 +41,8 @@ public class SpecifyTypeExplicitlyFix extends PsiElementBaseIntentionAction {
public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull PsiElement element) { public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull PsiElement element) {
//noinspection unchecked //noinspection unchecked
JetCallableDeclaration declaration = PsiTreeUtil.getParentOfType(element, JetProperty.class, JetNamedFunction.class); JetCallableDeclaration declaration = PsiTreeUtil.getParentOfType(element, JetProperty.class, JetNamedFunction.class);
JetType type = getTypeForDeclaration(declaration); JetType type = SpecifyTypeExplicitlyAction.Companion.getTypeForDeclaration(declaration);
addTypeAnnotation(project, editor, declaration, type); SpecifyTypeExplicitlyAction.Companion.addTypeAnnotation(project, editor, declaration, type);
} }
@Override @Override
@@ -61,6 +59,6 @@ public class SpecifyTypeExplicitlyFix extends PsiElementBaseIntentionAction {
return false; return false;
} }
return !getTypeForDeclaration(declaration).isError(); return !SpecifyTypeExplicitlyAction.Companion.getTypeForDeclaration(declaration).isError();
} }
} }
@@ -356,7 +356,7 @@ public class KotlinInplaceVariableIntroducer<D extends JetCallableDeclaration> e
if (typeReference != null) { if (typeReference != null) {
builder.replaceElement(typeReference, builder.replaceElement(typeReference,
TYPE_REFERENCE_VARIABLE_NAME, TYPE_REFERENCE_VARIABLE_NAME,
SpecifyTypeExplicitlyAction.createTypeExpressionForTemplate(myExprType), SpecifyTypeExplicitlyAction.Companion.createTypeExpressionForTemplate(myExprType),
false); false);
} }
} }
@@ -381,7 +381,7 @@ public class KotlinInplaceVariableIntroducer<D extends JetCallableDeclaration> e
TemplateState templateState = TemplateState templateState =
TemplateManagerImpl.getTemplateState(InjectedLanguageUtil.getTopLevelEditor(myEditor)); TemplateManagerImpl.getTemplateState(InjectedLanguageUtil.getTopLevelEditor(myEditor));
if (templateState != null && myDeclaration.getTypeReference() != null) { if (templateState != null && myDeclaration.getTypeReference() != null) {
templateState.addTemplateStateListener(SpecifyTypeExplicitlyAction.createTypeReferencePostprocessor(myDeclaration)); templateState.addTemplateStateListener(SpecifyTypeExplicitlyAction.Companion.createTypeReferencePostprocessor(myDeclaration));
} }
return result; return result;