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)
|
return KotlinCacheService.getInstance(getProject()).getLazyResolveSession(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun JetElement.getAnalysisResults(): AnalyzeExhaust {
|
public fun JetElement.getAnalysisResults(vararg extraFiles: JetFile): AnalyzeExhaust {
|
||||||
return KotlinCacheService.getInstance(getProject()).getAnalysisResults(listOf(this))
|
return KotlinCacheService.getInstance(getProject()).getAnalysisResults(listOf(this) + extraFiles.toList())
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun JetElement.getBindingContext(): BindingContext {
|
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 getGlobalCache(platform: TargetPlatform) = globalCachesPerPlatform[platform]!!.modulesCache
|
||||||
private fun getGlobalLibrariesCache(platform: TargetPlatform) = globalCachesPerPlatform[platform]!!.librariesCache
|
private fun getGlobalLibrariesCache(platform: TargetPlatform) = globalCachesPerPlatform[platform]!!.librariesCache
|
||||||
|
|
||||||
private val syntheticFileCaches = object : SLRUCache<JetFile, KotlinResolveCache>(2, 3) {
|
private val syntheticFileCaches = object : SLRUCache<Set<JetFile>, KotlinResolveCache>(2, 3) {
|
||||||
override fun createValue(file: JetFile?): KotlinResolveCache {
|
override fun createValue(files: Set<JetFile>): KotlinResolveCache {
|
||||||
val targetPlatform = TargetPlatformDetector.getPlatform(file!!)
|
// we assume that all files come from the same module
|
||||||
val syntheticFileModule = file.getModuleInfo()
|
val targetPlatform = files.map { TargetPlatformDetector.getPlatform(it) }.toSet().single()
|
||||||
|
val syntheticFileModule = files.map { it.getModuleInfo() }.toSet().single()
|
||||||
return when {
|
return when {
|
||||||
syntheticFileModule is ModuleSourceInfo -> {
|
syntheticFileModule is ModuleSourceInfo -> {
|
||||||
val dependentModules = syntheticFileModule.getDependentModules()
|
val dependentModules = syntheticFileModule.getDependentModules()
|
||||||
@@ -116,7 +117,7 @@ public class KotlinCacheService(val project: Project) {
|
|||||||
project,
|
project,
|
||||||
globalResolveSessionProvider(
|
globalResolveSessionProvider(
|
||||||
targetPlatform,
|
targetPlatform,
|
||||||
syntheticFiles = listOf(file),
|
syntheticFiles = files,
|
||||||
reuseDataFromCache = getGlobalCache(targetPlatform),
|
reuseDataFromCache = getGlobalCache(targetPlatform),
|
||||||
moduleFilter = { it in dependentModules },
|
moduleFilter = { it in dependentModules },
|
||||||
dependencies = listOf(PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT)
|
dependencies = listOf(PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT)
|
||||||
@@ -129,7 +130,7 @@ public class KotlinCacheService(val project: Project) {
|
|||||||
project,
|
project,
|
||||||
globalResolveSessionProvider(
|
globalResolveSessionProvider(
|
||||||
targetPlatform,
|
targetPlatform,
|
||||||
syntheticFiles = listOf(file),
|
syntheticFiles = files,
|
||||||
reuseDataFromCache = getGlobalLibrariesCache(targetPlatform),
|
reuseDataFromCache = getGlobalLibrariesCache(targetPlatform),
|
||||||
moduleFilter = { it == syntheticFileModule },
|
moduleFilter = { it == syntheticFileModule },
|
||||||
dependencies = listOf(PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT)
|
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
|
//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
|
// currently the only known scenario is when we cannot determine that file is a library source
|
||||||
// (file under both classes and sources root)
|
// (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(
|
KotlinResolveCache(
|
||||||
project,
|
project,
|
||||||
globalResolveSessionProvider(
|
globalResolveSessionProvider(
|
||||||
targetPlatform,
|
targetPlatform,
|
||||||
syntheticFiles = listOf(file),
|
syntheticFiles = files,
|
||||||
moduleFilter = { true },
|
moduleFilter = { true },
|
||||||
dependencies = listOf(PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT)
|
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) {
|
return synchronized(syntheticFileCaches) {
|
||||||
syntheticFileCaches[file]
|
syntheticFileCaches[files]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +172,7 @@ public class KotlinCacheService(val project: Project) {
|
|||||||
public fun getLazyResolveSession(element: JetElement): ResolveSessionForBodies {
|
public fun getLazyResolveSession(element: JetElement): ResolveSessionForBodies {
|
||||||
val file = element.getContainingJetFile()
|
val file = element.getContainingJetFile()
|
||||||
if (!ProjectRootsUtil.isInProjectSource(file)) {
|
if (!ProjectRootsUtil.isInProjectSource(file)) {
|
||||||
return getCacheForSyntheticFile(file).getLazyResolveSession(file)
|
return getCacheForSyntheticFile(setOf(file)).getLazyResolveSession(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
return getGlobalLazyResolveSession(file, TargetPlatformDetector.getPlatform(file))
|
return getGlobalLazyResolveSession(file, TargetPlatformDetector.getPlatform(file))
|
||||||
@@ -180,11 +181,15 @@ public class KotlinCacheService(val project: Project) {
|
|||||||
public fun getAnalysisResults(elements: Collection<JetElement>): AnalyzeExhaust {
|
public fun getAnalysisResults(elements: Collection<JetElement>): AnalyzeExhaust {
|
||||||
if (elements.isEmpty()) return AnalyzeExhaust.EMPTY
|
if (elements.isEmpty()) return AnalyzeExhaust.EMPTY
|
||||||
|
|
||||||
val firstFile = elements.first().getContainingJetFile()
|
val files = elements.map { it.getContainingJetFile() }.toSet()
|
||||||
if (elements.size == 1 && (!ProjectRootsUtil.isInProjectSource(firstFile) && firstFile !is JetCodeFragment)) {
|
if (files.all { !ProjectRootsUtil.isInProjectSource(it) }
|
||||||
return getCacheForSyntheticFile(firstFile).getAnalysisResultsForElements(elements)
|
// 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)
|
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 com.intellij.openapi.project.Project
|
||||||
import org.jetbrains.jet.lang.resolve.DescriptorUtils
|
import org.jetbrains.jet.lang.resolve.DescriptorUtils
|
||||||
import org.jetbrains.jet.codegen.StackValue
|
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 RECEIVER_NAME = "\$receiver"
|
||||||
private val THIS_NAME = "this"
|
private val THIS_NAME = "this"
|
||||||
@@ -232,9 +232,8 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment,
|
|||||||
return runReadAction {
|
return runReadAction {
|
||||||
val file = createFileForDebugger(codeFragment, extractedFunction)
|
val file = createFileForDebugger(codeFragment, extractedFunction)
|
||||||
|
|
||||||
file.checkForErrors()
|
val analyzeExhaust = file.checkForErrors()
|
||||||
|
|
||||||
val analyzeExhaust = file.getAnalysisResults()
|
|
||||||
val state = GenerationState(
|
val state = GenerationState(
|
||||||
file.getProject(),
|
file.getProject(),
|
||||||
ClassBuilderFactories.BINARIES,
|
ClassBuilderFactories.BINARIES,
|
||||||
@@ -259,7 +258,7 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment,
|
|||||||
throw EvaluateExceptionUtil.createEvaluateException(e)
|
throw EvaluateExceptionUtil.createEvaluateException(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun JetFile.checkForErrors() {
|
private fun JetFile.checkForErrors() =
|
||||||
runReadAction {
|
runReadAction {
|
||||||
try {
|
try {
|
||||||
AnalyzingUtils.checkForSyntacticErrors(this)
|
AnalyzingUtils.checkForSyntacticErrors(this)
|
||||||
@@ -268,7 +267,7 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment,
|
|||||||
throw EvaluateExceptionUtil.createEvaluateException(e.getMessage())
|
throw EvaluateExceptionUtil.createEvaluateException(e.getMessage())
|
||||||
}
|
}
|
||||||
|
|
||||||
val analyzeExhaust = this.getAnalysisResults()
|
val analyzeExhaust = this.getAnalysisResults(createFlexibleTypesFile())
|
||||||
if (analyzeExhaust.isError()) {
|
if (analyzeExhaust.isError()) {
|
||||||
throw EvaluateExceptionUtil.createEvaluateException(analyzeExhaust.getError())
|
throw EvaluateExceptionUtil.createEvaluateException(analyzeExhaust.getError())
|
||||||
}
|
}
|
||||||
@@ -277,8 +276,9 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment,
|
|||||||
bindingContext.getDiagnostics().firstOrNull { it.getSeverity() == Severity.ERROR }?.let {
|
bindingContext.getDiagnostics().firstOrNull { it.getSeverity() == Severity.ERROR }?.let {
|
||||||
throw EvaluateExceptionUtil.createEvaluateException(DefaultErrorMessages.RENDERER.render(it))
|
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")
|
assert(extractedFunctionText != null, "Text of extracted function shouldn't be null")
|
||||||
fileText = fileText.replace("!FUNCTION!", extractedFunction.getText()!!)
|
fileText = fileText.replace("!FUNCTION!", extractedFunction.getText()!!)
|
||||||
|
|
||||||
val virtualFile = LightVirtualFile("debugFile.kt", JetLanguage.INSTANCE, fileText)
|
val jetFile = codeFragment.createJetFile("debugFile.kt", fileText)
|
||||||
virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET)
|
|
||||||
val jetFile = (PsiFileFactory.getInstance(codeFragment.getProject()) as PsiFileFactoryImpl)
|
|
||||||
.trySetupPsiForFile(virtualFile, JetLanguage.INSTANCE, true, false) as JetFile
|
|
||||||
jetFile.skipVisibilityCheck = true
|
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
|
return jetFile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -107,7 +107,7 @@ fun getFunctionForExtractedFragment(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return validationResult.descriptor
|
return validationResult.descriptor
|
||||||
.generateDeclaration(ExtractionGeneratorOptions(inTempFile = true))
|
.generateDeclaration(ExtractionGeneratorOptions(inTempFile = true, flexibleTypesAllowed = true))
|
||||||
.declaration as JetNamedFunction
|
.declaration as JetNamedFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -318,7 +318,8 @@ data class ExtractableCodeDescriptor(
|
|||||||
|
|
||||||
data class ExtractionGeneratorOptions(
|
data class ExtractionGeneratorOptions(
|
||||||
val inTempFile: Boolean = false,
|
val inTempFile: Boolean = false,
|
||||||
val extractAsProperty: Boolean = false
|
val extractAsProperty: Boolean = false,
|
||||||
|
val flexibleTypesAllowed: Boolean = false
|
||||||
) {
|
) {
|
||||||
class object {
|
class object {
|
||||||
val DEFAULT = ExtractionGeneratorOptions()
|
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.JetPsiRange.Match
|
||||||
import org.jetbrains.jet.plugin.util.psi.patternMatching.UnificationResult.WeaklyMatched
|
import org.jetbrains.jet.plugin.util.psi.patternMatching.UnificationResult.WeaklyMatched
|
||||||
import org.jetbrains.jet.plugin.util.psi.patternMatching.UnificationResult.StronglyMatched
|
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.plugin.util.IdeDescriptorRenderers
|
||||||
import org.jetbrains.jet.lang.psi.psiUtil.parents
|
import org.jetbrains.jet.lang.psi.psiUtil.parents
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
@@ -64,7 +63,9 @@ import java.util.ArrayList
|
|||||||
fun ExtractableCodeDescriptor.getDeclarationText(
|
fun ExtractableCodeDescriptor.getDeclarationText(
|
||||||
options: ExtractionGeneratorOptions = ExtractionGeneratorOptions.DEFAULT,
|
options: ExtractionGeneratorOptions = ExtractionGeneratorOptions.DEFAULT,
|
||||||
withBody: Boolean = true,
|
withBody: Boolean = true,
|
||||||
descriptorRenderer: DescriptorRenderer = IdeDescriptorRenderers.SOURCE_CODE
|
descriptorRenderer: DescriptorRenderer = if (options.flexibleTypesAllowed)
|
||||||
|
DescriptorRenderer.FLEXIBLE_TYPES_FOR_CODE
|
||||||
|
else IdeDescriptorRenderers.SOURCE_CODE
|
||||||
): String {
|
): String {
|
||||||
if (!canGenerateProperty() && options.extractAsProperty) {
|
if (!canGenerateProperty() && options.extractAsProperty) {
|
||||||
throw IllegalArgumentException("Can't generate property: ${extractionData.getCodeFragmentText()}")
|
throw IllegalArgumentException("Can't generate property: ${extractionData.getCodeFragmentText()}")
|
||||||
@@ -344,7 +345,7 @@ fun ExtractableCodeDescriptor.generateDeclaration(options: ExtractionGeneratorOp
|
|||||||
fun createDeclaration(): JetNamedDeclaration {
|
fun createDeclaration(): JetNamedDeclaration {
|
||||||
return with(extractionData) {
|
return with(extractionData) {
|
||||||
if (options.inTempFile) {
|
if (options.inTempFile) {
|
||||||
createTemporaryDeclaration("${getDeclarationText()}\n")
|
createTemporaryDeclaration("${getDeclarationText(options)}\n")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
psiFactory.createDeclaration(getDeclarationText(options))
|
psiFactory.createDeclaration(getDeclarationText(options))
|
||||||
|
|||||||
+1
-1
@@ -276,6 +276,6 @@ public class KotlinExtractFunctionDialog extends DialogWrapper {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public ExtractionGeneratorOptions getGeneratorOptions() {
|
public ExtractionGeneratorOptions getGeneratorOptions() {
|
||||||
return new ExtractionGeneratorOptions(false, propertyCheckBox.isSelected());
|
return new ExtractionGeneratorOptions(false, propertyCheckBox.isSelected(), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user