Code corrections on code review

This commit is contained in:
Valentin Kipyatkov
2015-04-09 18:56:06 +03:00
parent 498f24b98b
commit a0d2b00287
10 changed files with 91 additions and 81 deletions
@@ -75,7 +75,7 @@ public class JavaToKotlinAction : AnAction() {
ProgressManager.getInstance().runProcessWithProgressSynchronously(
{
runReadAction {
externalCodeUpdate = converterResult!!.externalCodeProcessing(ProgressManager.getInstance().getProgressIndicator())
externalCodeUpdate = converterResult!!.externalCodeProcessing.prepareWriteOperation(ProgressManager.getInstance().getProgressIndicator())
}
},
title,
@@ -89,7 +89,7 @@ public class JavaToKotlinAction : AnAction() {
val newFiles = saveResults(javaFiles, converterResult!!.results.map { it!!.text /*conversion of a file always succeeds*/ })
externalCodeUpdate?.invoke()
externalCodeUpdate!!.invoke()
newFiles.singleOrNull()?.let {
FileEditorManager.getInstance(project).openFile(it, true)
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.codeInsight
import com.intellij.codeInsight.CodeInsightSettings
import com.intellij.codeInsight.editorActions.CopyPastePostProcessor
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.RangeMarker
@@ -28,7 +29,9 @@ import com.intellij.openapi.util.Ref
import com.intellij.openapi.util.TextRange
import com.intellij.psi.*
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.codeInsight.shorten.performDelayedShortening
@@ -51,7 +54,6 @@ import org.jetbrains.kotlin.resolve.ImportPath
import org.jetbrains.kotlin.resolve.JetModuleUtil
import org.jetbrains.kotlin.resolve.QualifiedExpressionResolver
import org.jetbrains.kotlin.resolve.QualifiedExpressionResolver.LookupMode
import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.types.ErrorUtils
@@ -268,7 +270,7 @@ public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<Kotlin
if (referencedFqNames.singleOrNull() == originalFqName) return null
// check that descriptor to import exists and is accessible from the current module
if (!findImportableDescriptors(originalFqName, file).any { KotlinReferenceData.Kind.fromDescriptor(it) == refData.kind }) {
if (findImportableDescriptors(originalFqName, file).none { KotlinReferenceData.Kind.fromDescriptor(it) == refData.kind }) {
return null
}
@@ -327,7 +329,9 @@ public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<Kotlin
private fun showRestoreReferencesDialog(project: Project, referencesToRestore: List<ReferenceToRestoreData>): Collection<ReferenceToRestoreData> {
val fqNames = referencesToRestore.map { it.refData.fqName }.toSortedSet()
declarationsToImportSuggested = fqNames
if (ApplicationManager.getApplication().isUnitTestMode()) {
declarationsToImportSuggested = fqNames
}
val shouldShowDialog = CodeInsightSettings.getInstance().ADD_IMPORTS_ON_PASTE == CodeInsightSettings.ASK
if (!shouldShowDialog || referencesToRestore.isEmpty()) {
@@ -352,7 +356,7 @@ public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<Kotlin
}
companion object {
// for tests
[TestOnly]
public var declarationsToImportSuggested: Collection<String> = emptyList()
}
}
@@ -29,6 +29,7 @@ import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiJavaFile
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.idea.codeInsight.KotlinCopyPasteReferenceProcessor
import org.jetbrains.kotlin.idea.codeInsight.KotlinReferenceData
import org.jetbrains.kotlin.idea.editor.JetEditorOptions
@@ -223,7 +224,7 @@ public class ConvertJavaCopyPastePostProcessor : CopyPastePostProcessor<TextBloc
}
companion object {
// used for testing
[TestOnly]
public var conversionPerformed: Boolean = false
}
}
@@ -57,14 +57,15 @@ data class DataForConversion private(
}
private fun clipTextIfNeeded(file: PsiJavaFile, fileText: String, startOffsets: IntArray, endOffsets: IntArray): String? {
fun canDropRange(range: TextRange)
= startOffsets.indices.all { range !in TextRange(startOffsets[it], endOffsets[it]) }
val ranges = startOffsets.indices.map { TextRange(startOffsets[it], endOffsets[it]) }.sortBy { it.start }
fun canDropRange(range: TextRange) = ranges.all { range !in it }
val rangesToDrop = ArrayList<TextRange>()
for (i in startOffsets.indices) {
val start = startOffsets[i]
val end = endOffsets[i]
val range = TextRange(start, end)
for (range in ranges) {
val start = range.start
val end = range.end
if (start == end) continue
val startToken = file.findElementAt(start)!!
val elementToClipLeft = startToken.maximalParentToClip(range)
@@ -197,7 +198,7 @@ data class DataForConversion private(
if (!canDropElementFromText(child)) return null
clipTo = childRange.end
}
else { // rangeBound is inside element's range
else { // rangeBound is inside child's range
if (child is PsiWhiteSpace) break // no need to cut whitespace - we can leave it as is
return tryClipSide(child, rangeBound, rangeFunction, childrenFunction)
}
@@ -93,7 +93,7 @@ class ClassBodyConverter(private val psiClass: PsiClass,
}
}
return ClassBody(null, null, convertedMembers.values().toList(), emptyList(), lBrace, rBrace, overloadReducer)
return ClassBody(null, null, convertedMembers.values().toList(), emptyList(), lBrace, rBrace)
}
val useCompanionObject = shouldGenerateCompanionObject(convertedMembers)
@@ -128,7 +128,7 @@ class ClassBodyConverter(private val psiClass: PsiClass,
primaryConstructorSignature = null // no "()" after class name is needed in this case
}
return ClassBody(primaryConstructorSignature, constructorConverter?.baseClassParams, members, companionObjectMembers, lBrace, rBrace, overloadReducer)
return ClassBody(primaryConstructorSignature, constructorConverter?.baseClassParams, members, companionObjectMembers, lBrace, rBrace)
}
private fun Converter.convertMember(member: PsiMember,
@@ -277,7 +277,7 @@ class Converter private(
// to convert fields and nested types - they are not allowed in Kotlin but we convert them and let user refactor code
var classBody = ClassBodyConverter(psiClass, this, isOpenClass = false, isObject = false).convertBody()
classBody = ClassBody(constructorSignature, classBody.baseClassParams, classBody.members,
classBody.companionObjectMembers, classBody.lBrace, classBody.rBrace, classBody.overloadReducer)
classBody.companionObjectMembers, classBody.lBrace, classBody.rBrace)
val annotationAnnotation = Annotation(Identifier("annotation").assignNoPrototype(), listOf(), false, false).assignNoPrototype()
return Class(psiClass.declarationIdentifier(),
@@ -40,7 +40,10 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.resolve.BindingContext
import java.util.*
import java.util.ArrayList
import java.util.Comparator
import java.util.LinkedHashMap
import kotlin.properties.Delegates
public trait PostProcessor {
public fun analyzeFile(file: JetFile, range: TextRange?): BindingContext
@@ -88,7 +91,11 @@ public class JavaToKotlinConverter(private val project: Project,
public data class ElementResult(val text: String, val parseContext: ParseContext)
public data class Result(val results: List<ElementResult?>, val externalCodeProcessing: ((ProgressIndicator) -> (() -> Unit)?)?) //TODO: change interface to not perform write-actions under progress
public trait ExternalCodeProcessing {
public fun prepareWriteOperation(progress: ProgressIndicator): () -> Unit
}
public data class Result(val results: List<ElementResult?>, val externalCodeProcessing: ExternalCodeProcessing?)
public fun elementsToKotlin(
inputElements: List<InputElement>,
@@ -200,12 +207,14 @@ public class JavaToKotlinConverter(private val project: Project,
val target: PsiElement,
val file: PsiFile,
val processings: Collection<UsageProcessing>
)
) {
val depth: Int by Delegates.lazy { target.parents(withItself = true).takeWhile { it !is PsiFile }.count() }
}
private fun buildExternalCodeProcessing(
usageProcessings: Map<PsiElement, Collection<UsageProcessing>>,
inConversionScope: (PsiElement) -> Boolean
): ((ProgressIndicator) -> (() -> Unit)?)? {
): ExternalCodeProcessing? {
if (usageProcessings.isEmpty()) return null
val map: Map<PsiElement, Collection<UsageProcessing>> = usageProcessings.values()
@@ -214,51 +223,53 @@ public class JavaToKotlinConverter(private val project: Project,
.groupBy { it.targetElement }
if (map.isEmpty()) return null
return fun(progress: ProgressIndicator): (() -> Unit)? {
val refs = ArrayList<ReferenceInfo>()
return object: ExternalCodeProcessing {
override fun prepareWriteOperation(progress: ProgressIndicator): () -> Unit {
val refs = ArrayList<ReferenceInfo>()
progress.setText("Searching usages to update...")
progress.setText("Searching usages to update...")
for ((i, entry) in map.entrySet().withIndex()) {
val psiElement = entry.key
val processings = entry.value
for ((i, entry) in map.entrySet().withIndex()) {
val psiElement = entry.key
val processings = entry.value
progress.setText2((psiElement as? PsiNamedElement)?.getName() ?: "")
progress.checkCanceled()
progress.setText2((psiElement as? PsiNamedElement)?.getName() ?: "")
progress.checkCanceled()
ProgressManager.getInstance().runProcess(
{
val searchJava = processings.any { it.javaCodeProcessor != null }
val searchKotlin = processings.any { it.kotlinCodeProcessor != null }
referenceSearcher.findExternalCodeProcessingUsages(psiElement, searchJava, searchKotlin)
.filterNot { inConversionScope(it.getElement()) }
.mapTo(refs) { ReferenceInfo(it, psiElement, it.getElement().getContainingFile(), processings) }
},
ProgressPortionReporter(progress, i / map.size().toDouble(), 1.0 / map.size()))
ProgressManager.getInstance().runProcess(
{
val searchJava = processings.any { it.javaCodeProcessor != null }
val searchKotlin = processings.any { it.kotlinCodeProcessor != null }
referenceSearcher.findUsagesForExternalCodeProcessing(psiElement, searchJava, searchKotlin)
.filterNot { inConversionScope(it.getElement()) }
.mapTo(refs) { ReferenceInfo(it, psiElement, it.getElement().getContainingFile(), processings) }
},
ProgressPortionReporter(progress, i / map.size().toDouble(), 1.0 / map.size()))
}
return { processUsages(refs) }
}
if (refs.isEmpty()) return null
return { processUsages(refs) }
}
}
private fun processUsages(refs: Collection<ReferenceInfo>) {
@ReferenceLoop
for ((reference, target, file, processings) in refs.sortBy(ReferenceComparator)) {
val processors = when (reference.getElement().getLanguage()) {
JavaLanguage.INSTANCE -> processings.map { it.javaCodeProcessor }.filterNotNull()
JetLanguage.INSTANCE -> processings.map { it.kotlinCodeProcessor }.filterNotNull()
else -> continue@ReferenceLoop
}
for (fileRefs in refs.groupBy { it.file }.values()) { // group by file for faster sorting
@ReferenceLoop
for ((reference, target, file, processings) in fileRefs.sortBy(ReferenceComparator)) {
val processors = when (reference.getElement().getLanguage()) {
JavaLanguage.INSTANCE -> processings.map { it.javaCodeProcessor }.filterNotNull()
JetLanguage.INSTANCE -> processings.map { it.kotlinCodeProcessor }.filterNotNull()
else -> continue@ReferenceLoop
}
checkReferenceValid(reference)
checkReferenceValid(reference)
var references = listOf(reference)
for (processor in processors) {
references = references.flatMap { processor.processUsage(it) ?: listOf(it) }
references.forEach { checkReferenceValid(it) }
var references = listOf(reference)
for (processor in processors) {
references = references.flatMap { processor.processUsage(it) ?: listOf(it) }
references.forEach { checkReferenceValid(it) }
}
}
}
}
@@ -273,18 +284,15 @@ public class JavaToKotlinConverter(private val project: Project,
val element1 = info1.reference.getElement()
val element2 = info2.reference.getElement()
val deepness1 = element1.deepnessInTree()
val deepness2 = element2.deepnessInTree()
if (deepness1 != deepness2) { // put deeper elements first to not invalidate them when processing ancestors
return -deepness1.compareTo(deepness2)
val depth1 = info1.depth
val depth2 = info2.depth
if (depth1 != depth2) { // put deeper elements first to not invalidate them when processing ancestors
return -depth1.compareTo(depth2)
}
// process elements of the same deepness from right to left so that right-side of assignments is not invalidated by processing of the left one
// process elements with the same parent from right to left so that right-side of assignments is not invalidated by processing of the left one
return -element1.getStartOffsetInParent().compareTo(element2.getStartOffsetInParent())
}
private fun PsiElement.deepnessInTree() = parents(withItself = true).takeWhile { it !is PsiFile }.count()
}
private class ProgressPortionReporter(
@@ -17,23 +17,22 @@
package org.jetbrains.kotlin.j2k
import com.intellij.lang.java.JavaLanguage
import com.intellij.openapi.fileTypes.FileTypeManager
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.fileTypes.FileType
import com.intellij.psi.*
import com.intellij.psi.search.DelegatingGlobalSearchScope
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.searches.ClassInheritorsSearch
import com.intellij.psi.search.searches.OverridingMethodsSearch
import com.intellij.psi.search.searches.ReferencesSearch
import org.jetbrains.kotlin.idea.JetLanguage
import java.util.ArrayList
public trait ReferenceSearcher {
fun findLocalUsages(element: PsiElement, scope: PsiElement): Collection<PsiReference>
fun hasInheritors(`class`: PsiClass): Boolean
fun hasOverrides(method: PsiMethod): Boolean
fun findExternalCodeProcessingUsages(element: PsiElement, searchJava: Boolean, searchKotlin: Boolean): Collection<PsiReference>
fun findUsagesForExternalCodeProcessing(element: PsiElement, searchJava: Boolean, searchKotlin: Boolean): Collection<PsiReference>
}
public fun ReferenceSearcher.findVariableUsages(variable: PsiVariable, scope: PsiElement): Collection<PsiReferenceExpression>
@@ -55,7 +54,7 @@ public object EmptyReferenceSearcher: ReferenceSearcher {
override fun findLocalUsages(element: PsiElement, scope: PsiElement): Collection<PsiReference> = emptyList()
override fun hasInheritors(`class`: PsiClass) = false
override fun hasOverrides(method: PsiMethod) = false
override fun findExternalCodeProcessingUsages(element: PsiElement, searchJava: Boolean, searchKotlin: Boolean): Collection<PsiReference>
override fun findUsagesForExternalCodeProcessing(element: PsiElement, searchJava: Boolean, searchKotlin: Boolean): Collection<PsiReference>
= throw UnsupportedOperationException()
}
@@ -66,17 +65,15 @@ public object IdeaReferenceSearcher: ReferenceSearcher {
override fun hasOverrides(method: PsiMethod) = OverridingMethodsSearch.search(method, false).any()
override fun findExternalCodeProcessingUsages(element: PsiElement, searchJava: Boolean, searchKotlin: Boolean): Collection<PsiReference> {
val searchScope = object : DelegatingGlobalSearchScope(GlobalSearchScope.projectScope(element.getProject())) {
override fun contains(file: VirtualFile): Boolean {
if (!super.contains(file)) return false
return when (FileTypeManager.getInstance().getFileTypeByFile(file)) {
JavaLanguage.INSTANCE.getAssociatedFileType() -> searchJava
JetLanguage.INSTANCE.getAssociatedFileType() -> searchKotlin
else -> false
}
}
override fun findUsagesForExternalCodeProcessing(element: PsiElement, searchJava: Boolean, searchKotlin: Boolean): Collection<PsiReference> {
val fileTypes = ArrayList<FileType>()
if (searchJava) {
fileTypes.add(JavaLanguage.INSTANCE.getAssociatedFileType())
}
if (searchKotlin) {
fileTypes.add(JetLanguage.INSTANCE.getAssociatedFileType())
}
val searchScope = GlobalSearchScope.getScopeRestrictedByFileTypes(GlobalSearchScope.projectScope(element.getProject()), *fileTypes.copyToArray())
return ReferencesSearch.search(element, searchScope).findAll()
}
}
@@ -30,8 +30,7 @@ class ClassBody (
val members: List<Member>,
val companionObjectMembers: List<Member>,
val lBrace: LBrace,
val rBrace: RBrace,
val overloadReducer: OverloadReducer) {
val rBrace: RBrace) {
fun append(builder: CodeBuilder) {
val membersFiltered = members.filter { !it.isEmpty }
@@ -59,7 +59,7 @@ public abstract class AbstractJavaToKotlinConverterMultiFileTest : AbstractJavaT
val inputElements = psiFilesToConvert.map { JavaToKotlinConverter.InputElement(it, it) }
val (results, externalCodeProcessor) = converter.elementsToKotlin(inputElements)
val process = externalCodeProcessor?.invoke(EmptyProgressIndicator())
val process = externalCodeProcessor?.prepareWriteOperation(EmptyProgressIndicator())
project.executeWriteCommand("") { process?.invoke() }
fun expectedResultFile(i: Int) = File(filesToConvert[i].getPath().replace(".java", ".kt"))