Implement modules in IDE

IDE:
Rewrite AnalyzerFacade and implementations for JS and JVM to support creating separate analyzers for each module
Introduce ModuleInfo which is an intermediate entity between configuration (tests or idea modules) and ModuleDescriptor
Implement IdeaModuleInfos which represent IDEA modules, sdks and libraries
Add (somewhat thin) test checking their behaviour
Implement getModuleInfo() - utility to obtain IdeaModuleInfo for PsiElement
Drop Project.getLazyResolveSession() - not possible to obtain resolve session for the whole project any more
Adjust JavaResolveExtension accordingly
KotlinSignature Intention/Marker - make sure that analyzed element is cls element (he's not in resolve scope otherwise)

LightClasses:
Create separate package light classes for each module
Java code can only reference light class from the first module among it's dependencies
Duplicate jvm signature is only reported on package declarations inside one module

Injectors:
Receive GlobalSearchScope as paramer for VirtualFileFinder and JavaClassFinder
which allows to narrow analyzer scope

JDR:
Introduce ModuleClassResolver resolves java classes in correct java descriptor resolver (corresponding ModuleDescriptor)
Add test checking that java classes belong to correct module

Debugger:
Provide context to analyze files created by debugger in

Converter:
Postprocessor now needs a context to analyze resulting code in

JetPsiFactory:
Add verification that files created by psi factory are not analyzed without context (that is almost never a good idea)

Other:
Use new API in various tests, utilities, run configuration producers and builtin serializers
Various "TODO: (module refactoring)" which mark the unfinished parts
This commit is contained in:
Pavel V. Talanov
2014-06-10 16:50:35 +04:00
parent 07935c837a
commit db5303c019
82 changed files with 1813 additions and 527 deletions
@@ -56,7 +56,8 @@ public class JavaToKotlinAction extends AnAction {
final Converter converter = Converter.OBJECT$.create(project,
ConverterSettings.defaultSettings,
new FilesConversionScope(selectedJavaFiles),
J2kPostProcessor.INSTANCE$);
//TODO: (module refactoring) resulting files should be analyzed in context of respective java files
new J2kPostProcessor(selectedJavaFiles.iterator().next()));
CommandProcessor.getInstance().executeCommand(
project,
new Runnable() {
@@ -1,33 +0,0 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.plugin.caches.resolve
import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver
import org.jetbrains.jet.analyzer.AnalyzerFacade
import org.jetbrains.jet.lang.resolve.java.AnalyzerFacadeForJVM.JvmSetup
import org.jetbrains.jet.plugin.project.TargetPlatform
import com.intellij.openapi.project.Project
object JavaResolveExtension : CacheExtension<JavaDescriptorResolver> {
override val platform: TargetPlatform = TargetPlatform.JVM
override fun getData(setup: AnalyzerFacade.Setup): JavaDescriptorResolver {
return (setup as JvmSetup).getJavaDescriptorResolver()
}
public fun get(project: Project): JavaDescriptorResolver = KotlinCacheService.getInstance(project)[this]
}
@@ -70,7 +70,7 @@ public class ConvertJavaCopyPastePostProcessor() : CopyPastePostProcessor<TextBl
val jetEditorOptions = JetEditorOptions.getInstance()!!
val needConvert = jetEditorOptions.isEnableJavaToKotlinConversion() && (jetEditorOptions.isDonTShowConversionDialog() || okFromDialog(project))
if (needConvert) {
val text = convertCopiedCodeToKotlin(value, sourceFile)
val text = convertCopiedCodeToKotlin(value, sourceFile, targetFile)
if (text.isNotEmpty()) {
ApplicationManager.getApplication()!!.runWriteAction {
val startOffset = bounds.getStartOffset()
@@ -84,11 +84,11 @@ public class ConvertJavaCopyPastePostProcessor() : CopyPastePostProcessor<TextBl
}
}
private fun convertCopiedCodeToKotlin(code: CopiedCode, file: PsiJavaFile): String {
val converter = Converter.create(file.getProject(),
private fun convertCopiedCodeToKotlin(code: CopiedCode, fileCopiedFrom: PsiJavaFile, fileCopiedTo: JetFile): String {
val converter = Converter.create(fileCopiedFrom.getProject(),
ConverterSettings.defaultSettings,
FilesConversionScope(listOf(file)),
J2kPostProcessor)
FilesConversionScope(listOf(fileCopiedFrom)),
J2kPostProcessor(fileCopiedTo))
val startOffsets = code.getStartOffsets()
val endOffsets = code.getEndOffsets()
assert(startOffsets.size == endOffsets.size) { "Must have the same size" }
@@ -96,7 +96,7 @@ public class ConvertJavaCopyPastePostProcessor() : CopyPastePostProcessor<TextBl
for (i in startOffsets.indices) {
val startOffset = startOffsets[i]
val endOffset = endOffsets[i]
result.append(convertRangeToKotlin(file, TextRange(startOffset, endOffset), converter))
result.append(convertRangeToKotlin(fileCopiedFrom, TextRange(startOffset, endOffset), converter))
}
return StringUtil.convertLineSeparators(result.toString())
}
@@ -102,7 +102,10 @@ class KotlinEvaluateExpressionCache(val project: Project) {
return runReadAction {
val classes = JavaPsiFacade.getInstance(project).findClasses(jvmName.asString(), GlobalSearchScope.allScope(project))
if (classes.isEmpty()) null else JavaResolveExtension[project].resolveClass(JavaClassImpl(classes.first()))
if (classes.isEmpty()) null else {
val clazz = classes.first()
JavaResolveExtension.getResolver(project, clazz).resolveClass(JavaClassImpl(clazz))
}
}
}
@@ -25,7 +25,6 @@ import org.jetbrains.jet.lang.resolve.AnalyzingUtils
import org.jetbrains.jet.codegen.state.GenerationState
import org.jetbrains.jet.codegen.ClassBuilderFactories
import org.jetbrains.jet.codegen.KotlinCodegenFacade
import com.intellij.openapi.application.ApplicationManager
import com.intellij.testFramework.LightVirtualFile
import org.jetbrains.jet.plugin.JetLanguage
import org.jetbrains.jet.lang.psi.JetFile
@@ -35,7 +34,6 @@ import com.intellij.openapi.vfs.CharsetToolkit
import org.jetbrains.org.objectweb.asm.tree.MethodNode
import org.jetbrains.org.objectweb.asm.Opcodes.ASM5
import org.jetbrains.org.objectweb.asm.*
import com.intellij.openapi.util.Computable
import org.jetbrains.eval4j.*
import org.jetbrains.eval4j.jdi.JDIEval
import org.jetbrains.eval4j.jdi.asJdiValue
@@ -62,6 +60,7 @@ import com.sun.jdi.VirtualMachine
import org.jetbrains.jet.codegen.AsmUtil
import com.sun.jdi.InvalidStackFrameException
import org.jetbrains.jet.plugin.refactoring.runReadAction
import org.jetbrains.jet.lang.psi.analysisContext
private val RECEIVER_NAME = "\$receiver"
private val THIS_NAME = "this"
@@ -278,6 +277,7 @@ private fun createFileForDebugger(codeFragment: JetCodeFragment,
val jetFile = (PsiFileFactory.getInstance(codeFragment.getProject()) as PsiFileFactoryImpl)
.trySetupPsiForFile(virtualFile, JetLanguage.INSTANCE, true, false) as JetFile
jetFile.skipVisibilityCheck = true
jetFile.analysisContext = codeFragment
return jetFile
}
@@ -22,8 +22,9 @@ import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.plugin.intentions.RemoveExplicitTypeArguments
import org.jetbrains.jet.plugin.caches.resolve.getAnalysisResults
import java.util.ArrayList
import com.intellij.psi.PsiElement
public object J2kPostProcessor : PostProcessor {
public class J2kPostProcessor(override val contextToAnalyzeIn: PsiElement) : PostProcessor {
override fun analyzeFile(file: JetFile): BindingContext {
return file.getAnalysisResults().getBindingContext()
}
@@ -106,7 +106,7 @@ public class KotlinSignatureAnnotationIntention extends BaseIntentionAction impl
return;
}
String signature = getDefaultSignature(project, annotatedElement);
String signature = getDefaultSignature(project, (PsiMember) KotlinSignatureUtil.getAnnotationOwner(annotatedElement));
final MessageBusConnection busConnection = project.getMessageBus().connect();
busConnection.subscribe(ExternalAnnotationsManager.TOPIC, new ExternalAnnotationsListener.Adapter() {
@@ -143,7 +143,7 @@ public class KotlinSignatureAnnotationIntention extends BaseIntentionAction impl
@NotNull
private static String getDefaultSignature(@NotNull Project project, @NotNull PsiMember psiMember) {
JavaDescriptorResolver javaDescriptorResolver = JavaResolveExtension.INSTANCE$.get(project);
JavaDescriptorResolver javaDescriptorResolver = JavaResolveExtension.INSTANCE$.getResolver(project, psiMember);
if (psiMember instanceof PsiMethod) {
PsiMethod psiMethod = (PsiMethod) psiMember;
@@ -31,6 +31,7 @@ import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.Function;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
@@ -44,9 +45,7 @@ import org.jetbrains.jet.lang.resolve.java.structure.impl.JavaFieldImpl;
import org.jetbrains.jet.lang.resolve.java.structure.impl.JavaMethodImpl;
import org.jetbrains.jet.plugin.JetIcons;
import org.jetbrains.jet.plugin.caches.resolve.JavaResolveExtension;
import org.jetbrains.jet.plugin.caches.resolve.ResolvePackage;
import org.jetbrains.jet.plugin.project.ProjectStructureUtil;
import org.jetbrains.jet.plugin.project.TargetPlatform;
import java.awt.event.MouseEvent;
import java.util.Collection;
@@ -76,7 +75,8 @@ public class KotlinSignatureInJavaMarkerProvider implements LineMarkerProvider {
return;
}
Project project = elements.get(0).getProject();
PsiElement firstElement = elements.get(0);
Project project = firstElement.getProject();
if (!isMarkersEnabled(project)) {
return;
}
@@ -85,17 +85,13 @@ public class KotlinSignatureInJavaMarkerProvider implements LineMarkerProvider {
return;
}
Module module = ModuleUtilCore.findModuleForPsiElement(elements.get(0));
Module module = ModuleUtilCore.findModuleForPsiElement(firstElement);
if (module != null && !ProjectStructureUtil.isUsedInKotlinJavaModule(module)) {
return;
}
BindingContext bindingContext = ResolvePackage.getLazyResolveSession(project, TargetPlatform.JVM).getBindingContext();
JavaDescriptorResolver javaDescriptorResolver = JavaResolveExtension.INSTANCE$.get(project);
for (PsiElement element : elements) {
if (!(element instanceof PsiMember)) {
if (!(element instanceof PsiMember) || element instanceof PsiClass) {
continue;
}
@@ -103,8 +99,18 @@ public class KotlinSignatureInJavaMarkerProvider implements LineMarkerProvider {
if (member.hasModifierProperty(PsiModifier.PRIVATE)) {
continue;
}
PsiClass containingClass = member.getContainingClass();
if (containingClass != null && PsiUtil.isLocalOrAnonymousClass(containingClass)) {
continue;
}
PsiModifierListOwner annotationOwner = KotlinSignatureUtil.getAnnotationOwner(element);
JavaResolveExtension resolveExtension = JavaResolveExtension.INSTANCE$;
BindingContext bindingContext = resolveExtension.getContext(project, annotationOwner);
JavaDescriptorResolver javaDescriptorResolver = resolveExtension.getResolver(project, annotationOwner);
DeclarationDescriptor memberDescriptor = getDescriptorForMember(javaDescriptorResolver, annotationOwner);
if (memberDescriptor == null) continue;
@@ -141,7 +141,7 @@ public fun PsiElement.isInJavaSourceRoot(): Boolean =
!JavaProjectRootsUtil.isOutsideJavaSourceRoot(getContainingFile())
public inline fun JetFile.createTempCopy(textTransform: (String) -> String): JetFile {
val tmpFile = JetPsiFactory(this).createFile(getName(), textTransform(getText() ?: ""))
val tmpFile = JetPsiFactory(this).createAnalyzableFile(getName(), textTransform(getText() ?: ""), this)
tmpFile.setOriginalFile(this)
tmpFile.skipVisibilityCheck = skipVisibilityCheck
return tmpFile
@@ -25,20 +25,20 @@ import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.NotNullFunction;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.analyzer.AnalyzerFacade;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.psi.JetNamedFunction;
import org.jetbrains.jet.lang.resolve.java.PackageClassUtils;
import org.jetbrains.jet.lang.resolve.lazy.ResolveSession;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.plugin.MainFunctionDetector;
import org.jetbrains.jet.plugin.ProjectRootsUtil;
import org.jetbrains.jet.plugin.project.AnalyzerFacadeProvider;
import org.jetbrains.jet.plugin.caches.resolve.ResolvePackage;
import org.jetbrains.jet.plugin.project.ProjectStructureUtil;
import org.jetbrains.jet.plugin.project.ResolveSessionForBodies;
import java.util.Collections;
import java.util.List;
public class JetRunConfigurationProducer extends RuntimeConfigurationProducer implements Cloneable {
@@ -83,11 +83,15 @@ public class JetRunConfigurationProducer extends RuntimeConfigurationProducer im
PsiFile psiFile = location.getPsiElement().getContainingFile();
if (psiFile instanceof JetFile) {
JetFile jetFile = (JetFile) psiFile;
AnalyzerFacade facade = AnalyzerFacadeProvider.getAnalyzerFacadeForFile(jetFile);
ResolveSession resolveSession =
facade.createSetup(jetFile.getProject(), Collections.<JetFile>emptyList(), GlobalSearchScope.fileScope(jetFile))
.getLazyResolveSession();
MainFunctionDetector mainFunctionDetector = new MainFunctionDetector(resolveSession);
final ResolveSessionForBodies session = ResolvePackage.getLazyResolveSession(jetFile);
MainFunctionDetector mainFunctionDetector = new MainFunctionDetector(
new NotNullFunction<JetNamedFunction, FunctionDescriptor>() {
@NotNull
@Override
public FunctionDescriptor fun(JetNamedFunction function) {
return (FunctionDescriptor) session.resolveToDescriptor(function);
}
});
if (mainFunctionDetector.hasMain(jetFile.getDeclarations())) {
return jetFile;
}