Flexible types supported in Extract Function when used from debugger/evaluate expression
This commit is contained in:
committed by
Pavel V. Talanov
parent
925177f3d8
commit
d4d909de5f
+21
-16
@@ -42,8 +42,8 @@ public fun JetElement.getLazyResolveSession(): ResolveSessionForBodies {
|
||||
return KotlinCacheService.getInstance(getProject()).getLazyResolveSession(this)
|
||||
}
|
||||
|
||||
public fun JetElement.getAnalysisResults(): AnalyzeExhaust {
|
||||
return KotlinCacheService.getInstance(getProject()).getAnalysisResults(listOf(this))
|
||||
public fun JetElement.getAnalysisResults(vararg extraFiles: JetFile): AnalyzeExhaust {
|
||||
return KotlinCacheService.getInstance(getProject()).getAnalysisResults(listOf(this) + extraFiles.toList())
|
||||
}
|
||||
|
||||
public fun JetElement.getBindingContext(): BindingContext {
|
||||
@@ -105,10 +105,11 @@ public class KotlinCacheService(val project: Project) {
|
||||
private fun getGlobalCache(platform: TargetPlatform) = globalCachesPerPlatform[platform]!!.modulesCache
|
||||
private fun getGlobalLibrariesCache(platform: TargetPlatform) = globalCachesPerPlatform[platform]!!.librariesCache
|
||||
|
||||
private val syntheticFileCaches = object : SLRUCache<JetFile, KotlinResolveCache>(2, 3) {
|
||||
override fun createValue(file: JetFile?): KotlinResolveCache {
|
||||
val targetPlatform = TargetPlatformDetector.getPlatform(file!!)
|
||||
val syntheticFileModule = file.getModuleInfo()
|
||||
private val syntheticFileCaches = object : SLRUCache<Set<JetFile>, KotlinResolveCache>(2, 3) {
|
||||
override fun createValue(files: Set<JetFile>): KotlinResolveCache {
|
||||
// we assume that all files come from the same module
|
||||
val targetPlatform = files.map { TargetPlatformDetector.getPlatform(it) }.toSet().single()
|
||||
val syntheticFileModule = files.map { it.getModuleInfo() }.toSet().single()
|
||||
return when {
|
||||
syntheticFileModule is ModuleSourceInfo -> {
|
||||
val dependentModules = syntheticFileModule.getDependentModules()
|
||||
@@ -116,7 +117,7 @@ public class KotlinCacheService(val project: Project) {
|
||||
project,
|
||||
globalResolveSessionProvider(
|
||||
targetPlatform,
|
||||
syntheticFiles = listOf(file),
|
||||
syntheticFiles = files,
|
||||
reuseDataFromCache = getGlobalCache(targetPlatform),
|
||||
moduleFilter = { it in dependentModules },
|
||||
dependencies = listOf(PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT)
|
||||
@@ -129,7 +130,7 @@ public class KotlinCacheService(val project: Project) {
|
||||
project,
|
||||
globalResolveSessionProvider(
|
||||
targetPlatform,
|
||||
syntheticFiles = listOf(file),
|
||||
syntheticFiles = files,
|
||||
reuseDataFromCache = getGlobalLibrariesCache(targetPlatform),
|
||||
moduleFilter = { it == syntheticFileModule },
|
||||
dependencies = listOf(PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT)
|
||||
@@ -141,12 +142,12 @@ public class KotlinCacheService(val project: Project) {
|
||||
//NOTE: this code should not be called for sdk or library classes
|
||||
// currently the only known scenario is when we cannot determine that file is a library source
|
||||
// (file under both classes and sources root)
|
||||
LOG.warn("Creating cache with synthetic file ($file) in classes of library $syntheticFileModule")
|
||||
LOG.warn("Creating cache with synthetic files ($files) in classes of library $syntheticFileModule")
|
||||
KotlinResolveCache(
|
||||
project,
|
||||
globalResolveSessionProvider(
|
||||
targetPlatform,
|
||||
syntheticFiles = listOf(file),
|
||||
syntheticFiles = files,
|
||||
moduleFilter = { true },
|
||||
dependencies = listOf(PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT)
|
||||
)
|
||||
@@ -158,9 +159,9 @@ public class KotlinCacheService(val project: Project) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCacheForSyntheticFile(file: JetFile): KotlinResolveCache {
|
||||
private fun getCacheForSyntheticFile(files: Set<JetFile>): KotlinResolveCache {
|
||||
return synchronized(syntheticFileCaches) {
|
||||
syntheticFileCaches[file]
|
||||
syntheticFileCaches[files]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +172,7 @@ public class KotlinCacheService(val project: Project) {
|
||||
public fun getLazyResolveSession(element: JetElement): ResolveSessionForBodies {
|
||||
val file = element.getContainingJetFile()
|
||||
if (!ProjectRootsUtil.isInProjectSource(file)) {
|
||||
return getCacheForSyntheticFile(file).getLazyResolveSession(file)
|
||||
return getCacheForSyntheticFile(setOf(file)).getLazyResolveSession(file)
|
||||
}
|
||||
|
||||
return getGlobalLazyResolveSession(file, TargetPlatformDetector.getPlatform(file))
|
||||
@@ -180,11 +181,15 @@ public class KotlinCacheService(val project: Project) {
|
||||
public fun getAnalysisResults(elements: Collection<JetElement>): AnalyzeExhaust {
|
||||
if (elements.isEmpty()) return AnalyzeExhaust.EMPTY
|
||||
|
||||
val firstFile = elements.first().getContainingJetFile()
|
||||
if (elements.size == 1 && (!ProjectRootsUtil.isInProjectSource(firstFile) && firstFile !is JetCodeFragment)) {
|
||||
return getCacheForSyntheticFile(firstFile).getAnalysisResultsForElements(elements)
|
||||
val files = elements.map { it.getContainingJetFile() }.toSet()
|
||||
if (files.all { !ProjectRootsUtil.isInProjectSource(it) }
|
||||
// TODO: decide what to do with this
|
||||
// && files.all { it !is JetCodeFragment }
|
||||
) {
|
||||
return getCacheForSyntheticFile(files).getAnalysisResultsForElements(elements)
|
||||
}
|
||||
|
||||
val firstFile = elements.first().getContainingJetFile()
|
||||
return getGlobalCache(TargetPlatformDetector.getPlatform(firstFile)).getAnalysisResultsForElements(elements)
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ import org.jetbrains.jet.lang.resolve.java.structure.impl.JavaClassImpl
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.jet.lang.resolve.DescriptorUtils
|
||||
import org.jetbrains.jet.codegen.StackValue
|
||||
import org.jetbrains.jet.analyzer.AnalyzeExhaust
|
||||
import org.jetbrains.jet.lang.types.Flexibility
|
||||
|
||||
private val RECEIVER_NAME = "\$receiver"
|
||||
private val THIS_NAME = "this"
|
||||
@@ -232,9 +232,8 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment,
|
||||
return runReadAction {
|
||||
val file = createFileForDebugger(codeFragment, extractedFunction)
|
||||
|
||||
file.checkForErrors()
|
||||
val analyzeExhaust = file.checkForErrors()
|
||||
|
||||
val analyzeExhaust = file.getAnalysisResults()
|
||||
val state = GenerationState(
|
||||
file.getProject(),
|
||||
ClassBuilderFactories.BINARIES,
|
||||
@@ -259,7 +258,7 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment,
|
||||
throw EvaluateExceptionUtil.createEvaluateException(e)
|
||||
}
|
||||
|
||||
private fun JetFile.checkForErrors() {
|
||||
private fun JetFile.checkForErrors() =
|
||||
runReadAction {
|
||||
try {
|
||||
AnalyzingUtils.checkForSyntacticErrors(this)
|
||||
@@ -268,7 +267,7 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment,
|
||||
throw EvaluateExceptionUtil.createEvaluateException(e.getMessage())
|
||||
}
|
||||
|
||||
val analyzeExhaust = this.getAnalysisResults()
|
||||
val analyzeExhaust = this.getAnalysisResults(createFlexibleTypesFile())
|
||||
if (analyzeExhaust.isError()) {
|
||||
throw EvaluateExceptionUtil.createEvaluateException(analyzeExhaust.getError())
|
||||
}
|
||||
@@ -277,8 +276,9 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment,
|
||||
bindingContext.getDiagnostics().firstOrNull { it.getSeverity() == Severity.ERROR }?.let {
|
||||
throw EvaluateExceptionUtil.createEvaluateException(DefaultErrorMessages.RENDERER.render(it))
|
||||
}
|
||||
|
||||
analyzeExhaust
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,12 +304,28 @@ private fun createFileForDebugger(codeFragment: JetCodeFragment,
|
||||
assert(extractedFunctionText != null, "Text of extracted function shouldn't be null")
|
||||
fileText = fileText.replace("!FUNCTION!", extractedFunction.getText()!!)
|
||||
|
||||
val virtualFile = LightVirtualFile("debugFile.kt", JetLanguage.INSTANCE, fileText)
|
||||
virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET)
|
||||
val jetFile = (PsiFileFactory.getInstance(codeFragment.getProject()) as PsiFileFactoryImpl)
|
||||
.trySetupPsiForFile(virtualFile, JetLanguage.INSTANCE, true, false) as JetFile
|
||||
val jetFile = codeFragment.createJetFile("debugFile.kt", fileText)
|
||||
jetFile.skipVisibilityCheck = true
|
||||
jetFile.analysisContext = codeFragment
|
||||
return jetFile
|
||||
}
|
||||
|
||||
private fun PsiElement.createFlexibleTypesFile(): JetFile {
|
||||
return createJetFile(
|
||||
"FLEXIBLE_TYPES.kt",
|
||||
"""
|
||||
package ${Flexibility.FLEXIBLE_TYPE_CLASSIFIER.getPackageFqName()}
|
||||
public class ${Flexibility.FLEXIBLE_TYPE_CLASSIFIER.getRelativeClassName()}<L, U>
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
private fun PsiElement.createJetFile(fileName: String, fileText: String): JetFile {
|
||||
// Not using JetPsiFactory because we need a virtual file attached to the JetFile
|
||||
val virtualFile = LightVirtualFile(fileName, JetLanguage.INSTANCE, fileText)
|
||||
virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET)
|
||||
val jetFile = (PsiFileFactory.getInstance(getProject()) as PsiFileFactoryImpl)
|
||||
.trySetupPsiForFile(virtualFile, JetLanguage.INSTANCE, true, false) as JetFile
|
||||
jetFile.analysisContext = this
|
||||
return jetFile
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -107,7 +107,7 @@ fun getFunctionForExtractedFragment(
|
||||
}
|
||||
|
||||
return validationResult.descriptor
|
||||
.generateDeclaration(ExtractionGeneratorOptions(inTempFile = true))
|
||||
.generateDeclaration(ExtractionGeneratorOptions(inTempFile = true, flexibleTypesAllowed = true))
|
||||
.declaration as JetNamedFunction
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -318,7 +318,8 @@ data class ExtractableCodeDescriptor(
|
||||
|
||||
data class ExtractionGeneratorOptions(
|
||||
val inTempFile: Boolean = false,
|
||||
val extractAsProperty: Boolean = false
|
||||
val extractAsProperty: Boolean = false,
|
||||
val flexibleTypesAllowed: Boolean = false
|
||||
) {
|
||||
class object {
|
||||
val DEFAULT = ExtractionGeneratorOptions()
|
||||
|
||||
@@ -56,7 +56,6 @@ import org.jetbrains.jet.plugin.util.psi.patternMatching.JetPsiRange
|
||||
import org.jetbrains.jet.plugin.util.psi.patternMatching.JetPsiRange.Match
|
||||
import org.jetbrains.jet.plugin.util.psi.patternMatching.UnificationResult.WeaklyMatched
|
||||
import org.jetbrains.jet.plugin.util.psi.patternMatching.UnificationResult.StronglyMatched
|
||||
import org.jetbrains.jet.lang.psi.JetDeclarationWithBody
|
||||
import org.jetbrains.jet.plugin.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.jet.lang.psi.psiUtil.parents
|
||||
import java.util.ArrayList
|
||||
@@ -64,7 +63,9 @@ import java.util.ArrayList
|
||||
fun ExtractableCodeDescriptor.getDeclarationText(
|
||||
options: ExtractionGeneratorOptions = ExtractionGeneratorOptions.DEFAULT,
|
||||
withBody: Boolean = true,
|
||||
descriptorRenderer: DescriptorRenderer = IdeDescriptorRenderers.SOURCE_CODE
|
||||
descriptorRenderer: DescriptorRenderer = if (options.flexibleTypesAllowed)
|
||||
DescriptorRenderer.FLEXIBLE_TYPES_FOR_CODE
|
||||
else IdeDescriptorRenderers.SOURCE_CODE
|
||||
): String {
|
||||
if (!canGenerateProperty() && options.extractAsProperty) {
|
||||
throw IllegalArgumentException("Can't generate property: ${extractionData.getCodeFragmentText()}")
|
||||
@@ -344,7 +345,7 @@ fun ExtractableCodeDescriptor.generateDeclaration(options: ExtractionGeneratorOp
|
||||
fun createDeclaration(): JetNamedDeclaration {
|
||||
return with(extractionData) {
|
||||
if (options.inTempFile) {
|
||||
createTemporaryDeclaration("${getDeclarationText()}\n")
|
||||
createTemporaryDeclaration("${getDeclarationText(options)}\n")
|
||||
}
|
||||
else {
|
||||
psiFactory.createDeclaration(getDeclarationText(options))
|
||||
|
||||
+1
-1
@@ -276,6 +276,6 @@ public class KotlinExtractFunctionDialog extends DialogWrapper {
|
||||
|
||||
@NotNull
|
||||
public ExtractionGeneratorOptions getGeneratorOptions() {
|
||||
return new ExtractionGeneratorOptions(false, propertyCheckBox.isSelected());
|
||||
return new ExtractionGeneratorOptions(false, propertyCheckBox.isSelected(), false);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user