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