Convert MoveDeclarationsOutHelper to Kotlin and minor improvements

This commit is contained in:
Nikolay Krasko
2018-12-12 16:35:42 +03:00
parent eda1f1f3a0
commit 8dd02126d9
4 changed files with 84 additions and 105 deletions
@@ -14,143 +14,122 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.codeInsight.surroundWith;
package org.jetbrains.kotlin.idea.codeInsight.surroundWith
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiUtilCore;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.VariableDescriptor;
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionUtils;
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils;
import org.jetbrains.kotlin.idea.core.ShortenReferences;
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.KotlinTypeKt;
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReference
import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.SearchScope
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.psi.util.PsiUtilCore
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.isError
import java.util.*
import java.util.ArrayList;
import java.util.List;
public class MoveDeclarationsOutHelper {
public static PsiElement[] move(@NotNull PsiElement container, @NotNull PsiElement[] statements, boolean generateDefaultInitializers) {
if (statements.length == 0) {
return statements;
object MoveDeclarationsOutHelper {
fun move(container: PsiElement, statements: Array<PsiElement>, generateDefaultInitializers: Boolean): Array<PsiElement> {
if (statements.isEmpty()) {
return statements
}
Project project = container.getProject();
val project = container.project
List<PsiElement> resultStatements = new ArrayList<PsiElement>();
List<KtProperty> propertiesDeclarations = new ArrayList<KtProperty>();
val resultStatements = ArrayList<PsiElement>()
val propertiesDeclarations = ArrayList<KtProperty>()
// Dummy element to add new declarations at the beginning
KtPsiFactory psiFactory = KtPsiFactoryKt.KtPsiFactory(project);
PsiElement dummyFirstStatement = container.addBefore(psiFactory.createExpression("dummyStatement"), statements[0]);
val psiFactory = KtPsiFactory(project)
val dummyFirstStatement = container.addBefore(psiFactory.createExpression("dummyStatement"), statements[0])
try {
SearchScope scope = new LocalSearchScope(container);
int lastStatementOffset = statements[statements.length - 1].getTextRange().getEndOffset();
val scope = LocalSearchScope(container)
val lastStatementOffset = statements[statements.size - 1].textRange.endOffset
for (PsiElement statement : statements) {
for (statement in statements) {
if (needToDeclareOut(statement, lastStatementOffset, scope)) {
if (statement instanceof KtProperty && ((KtProperty) statement).getInitializer() != null) {
KtProperty property = (KtProperty) statement;
KtProperty declaration = createVariableDeclaration(property, generateDefaultInitializers);
declaration = (KtProperty) container.addBefore(declaration, dummyFirstStatement);
propertiesDeclarations.add(declaration);
container.addAfter(psiFactory.createNewLine(), declaration);
val property = statement as? KtProperty
if (property?.initializer != null) {
var declaration = createVariableDeclaration(property, generateDefaultInitializers)
declaration = container.addBefore(declaration, dummyFirstStatement) as KtProperty
propertiesDeclarations.add(declaration)
container.addAfter(psiFactory.createNewLine(), declaration)
KtBinaryExpression assignment = createVariableAssignment(property);
resultStatements.add(property.replace(assignment));
val assignment = createVariableAssignment(property)
resultStatements.add(property.replace(assignment))
} else {
val newStatement = container.addBefore(statement, dummyFirstStatement)
container.addAfter(psiFactory.createNewLine(), newStatement)
container.deleteChildRange(statement, statement)
}
else {
PsiElement newStatement = container.addBefore(statement, dummyFirstStatement);
container.addAfter(psiFactory.createNewLine(), newStatement);
container.deleteChildRange(statement, statement);
}
}
else {
resultStatements.add(statement);
} else {
resultStatements.add(statement)
}
}
}
finally {
dummyFirstStatement.delete();
} finally {
dummyFirstStatement.delete()
}
ShortenReferences.DEFAULT.process(propertiesDeclarations);
ShortenReferences.DEFAULT.process(propertiesDeclarations)
return PsiUtilCore.toPsiElementArray(resultStatements);
return PsiUtilCore.toPsiElementArray(resultStatements)
}
@NotNull
private static KtBinaryExpression createVariableAssignment(@NotNull KtProperty property) {
String propertyName = property.getName();
assert propertyName != null : "Property should have a name " + property.getText();
KtBinaryExpression assignment = (KtBinaryExpression) KtPsiFactoryKt
.KtPsiFactory(property).createExpression(propertyName + " = x");
KtExpression right = assignment.getRight();
assert right != null : "Created binary expression should have a right part " + assignment.getText();
KtExpression initializer = property.getInitializer();
assert initializer != null : "Initializer should exist for property " + property.getText();
right.replace(initializer);
return assignment;
private fun createVariableAssignment(property: KtProperty): KtBinaryExpression {
val propertyName = property.name ?: error("Property should have a name " + property.text)
val assignment = KtPsiFactory(property).createExpression("$propertyName = x") as KtBinaryExpression
val right = assignment.right ?: error("Created binary expression should have a right part " + assignment.text)
val initializer = property.initializer ?: error("Initializer should exist for property " + property.text)
right.replace(initializer)
return assignment
}
@NotNull
private static KtProperty createVariableDeclaration(@NotNull KtProperty property, boolean generateDefaultInitializers) {
KotlinType propertyType = getPropertyType(property);
String defaultInitializer = null;
if (generateDefaultInitializers && property.isVar()) {
defaultInitializer = CodeInsightUtils.defaultInitializer(propertyType);
private fun createVariableDeclaration(property: KtProperty, generateDefaultInitializers: Boolean): KtProperty {
val propertyType = getPropertyType(property)
var defaultInitializer: String? = null
if (generateDefaultInitializers && property.isVar) {
defaultInitializer = CodeInsightUtils.defaultInitializer(propertyType)
}
return createProperty(property, propertyType, defaultInitializer);
return createProperty(property, propertyType, defaultInitializer)
}
@NotNull
private static KotlinType getPropertyType(@NotNull KtProperty property) {
VariableDescriptor variableDescriptor = ResolutionUtils.resolveToDescriptorIfAny(property, BodyResolveMode.PARTIAL);
assert variableDescriptor != null : "Couldn't resolve property to property descriptor " + property.getText();
return variableDescriptor.getType();
private fun getPropertyType(property: KtProperty): KotlinType {
val variableDescriptor = property.resolveToDescriptorIfAny(BodyResolveMode.PARTIAL)
?: error("Couldn't resolve property to property descriptor " + property.text)
return variableDescriptor.type
}
@NotNull
private static KtProperty createProperty(@NotNull KtProperty property, @NotNull KotlinType propertyType, @Nullable String initializer) {
KtTypeReference typeRef = property.getTypeReference();
String typeString = null;
private fun createProperty(property: KtProperty, propertyType: KotlinType, initializer: String?): KtProperty {
val typeRef = property.typeReference
var typeString: String? = null
if (typeRef != null) {
typeString = typeRef.getText();
}
else if (!KotlinTypeKt.isError(propertyType)) {
typeString = IdeDescriptorRenderers.SOURCE_CODE.renderType(propertyType);
typeString = typeRef.text
} else if (!propertyType.isError) {
typeString = IdeDescriptorRenderers.SOURCE_CODE.renderType(propertyType)
}
return KtPsiFactoryKt.KtPsiFactory(property).createProperty(property.getName(), typeString, property.isVar(), initializer);
return KtPsiFactory(property).createProperty(property.name!!, typeString, property.isVar, initializer)
}
private static boolean needToDeclareOut(@NotNull PsiElement element, int lastStatementOffset, @NotNull SearchScope scope) {
if (element instanceof KtProperty ||
element instanceof KtClassOrObject ||
element instanceof KtFunction) {
PsiReference[] refs = ReferencesSearch.search(element, scope, false).toArray(PsiReference.EMPTY_ARRAY);
if (refs.length > 0) {
PsiReference lastRef = refs[refs.length - 1];
if (lastRef.getElement().getTextOffset() > lastStatementOffset) {
return true;
private fun needToDeclareOut(element: PsiElement, lastStatementOffset: Int, scope: SearchScope): Boolean {
if (element is KtProperty ||
element is KtClassOrObject ||
element is KtFunction
) {
val refs = ReferencesSearch.search(element, scope, false).toArray(PsiReference.EMPTY_ARRAY)
if (refs.isNotEmpty()) {
val lastRef = refs[refs.size - 1]
if (lastRef.element.textOffset > lastStatementOffset) {
return true
}
}
}
return false;
}
private MoveDeclarationsOutHelper() {
return false
}
}
@@ -31,7 +31,7 @@ public class KotlinFunctionLiteralSurrounder extends KotlinStatementsSurrounder
@Nullable
@Override
protected TextRange surroundStatements(Project project, Editor editor, PsiElement container, PsiElement[] statements) {
statements = MoveDeclarationsOutHelper.move(container, statements, true);
statements = MoveDeclarationsOutHelper.INSTANCE.move(container, statements, true);
if (statements.length == 0) {
KotlinSurrounderUtils.showErrorHint(project, editor, KotlinSurrounderUtils.SURROUND_WITH_ERROR);
@@ -41,7 +41,7 @@ public abstract class KotlinIfSurrounderBase extends KotlinStatementsSurrounder
@Nullable
@Override
protected TextRange surroundStatements(Project project, Editor editor, PsiElement container, PsiElement[] statements) {
statements = MoveDeclarationsOutHelper.move(container, statements, isGenerateDefaultInitializers());
statements = MoveDeclarationsOutHelper.INSTANCE.move(container, statements, isGenerateDefaultInitializers());
if (statements.length == 0) {
KotlinSurrounderUtils.showErrorHint(project, editor, KotlinSurrounderUtils.SURROUND_WITH_ERROR);
@@ -37,7 +37,7 @@ public abstract class KotlinTrySurrounderBase extends KotlinStatementsSurrounder
@Nullable
@Override
protected TextRange surroundStatements(@NotNull Project project, @NotNull Editor editor, @NotNull PsiElement container, @NotNull PsiElement[] statements) {
statements = MoveDeclarationsOutHelper.move(container, statements, true);
statements = MoveDeclarationsOutHelper.INSTANCE.move(container, statements, true);
if (statements.length == 0) {
KotlinSurrounderUtils.showErrorHint(project, editor, KotlinSurrounderUtils.SURROUND_WITH_ERROR);