Artifact for IDEA plugin

This commit is contained in:
Maxim Shafirov
2012-01-12 18:42:40 +04:00
parent b79f3ffb40
commit db59319f95
10 changed files with 45 additions and 9 deletions
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="jdk" jdkName="IDEA 10.x" jdkType="IDEA JDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="backend" />
<orderEntry type="module" module-name="frontend" />
<orderEntry type="module" module-name="frontend.java" />
</component>
</module>
@@ -0,0 +1,122 @@
/*
* @author max
*/
package org.jetbrains.jet.asJava;
import com.intellij.openapi.util.Key;
import com.intellij.psi.*;
import com.intellij.psi.impl.compiled.ClsClassImpl;
import com.intellij.psi.impl.compiled.ClsEnumConstantImpl;
import com.intellij.psi.impl.compiled.ClsFieldImpl;
import com.intellij.psi.impl.compiled.ClsMethodImpl;
import com.intellij.psi.impl.java.stubs.*;
import com.intellij.psi.stubs.StubBase;
import org.jetbrains.annotations.NotNull;
class ClsWrapperStubPsiFactory extends StubPsiFactory {
public static final Key<PsiElement> ORIGIN_ELEMENT = Key.create("ORIGIN_ELEMENT");
private final StubPsiFactory delegate = new ClsStubPsiFactory();
@Override
public PsiClass createClass(PsiClassStub stub) {
final PsiElement origin = ((StubBase) stub).getUserData(ORIGIN_ELEMENT);
if (origin == null) return delegate.createClass(stub);
return new ClsClassImpl(stub) {
@NotNull
@Override
public PsiElement getNavigationElement() {
return origin;
}
};
}
@Override
public PsiAnnotation createAnnotation(PsiAnnotationStub stub) {
return delegate.createAnnotation(stub);
}
@Override
public PsiClassInitializer createClassInitializer(PsiClassInitializerStub stub) {
return delegate.createClassInitializer(stub);
}
@Override
public PsiReferenceList createClassReferenceList(PsiClassReferenceListStub stub) {
return delegate.createClassReferenceList(stub);
}
@Override
public PsiField createField(PsiFieldStub stub) {
final PsiElement origin = ((StubBase) stub).getUserData(ORIGIN_ELEMENT);
if (origin == null) return delegate.createField(stub);
if (stub.isEnumConstant()) {
return new ClsEnumConstantImpl(stub) {
@NotNull
@Override
public PsiElement getNavigationElement() {
return origin;
}
};
}
else {
return new ClsFieldImpl(stub) {
@NotNull
@Override
public PsiElement getNavigationElement() {
return origin;
}
};
}
}
@Override
public PsiImportList createImportList(PsiImportListStub stub) {
return delegate.createImportList(stub);
}
@Override
public PsiImportStatementBase createImportStatement(PsiImportStatementStub stub) {
return delegate.createImportStatement(stub);
}
@Override
public PsiMethod createMethod(PsiMethodStub stub) {
final PsiElement origin = ((StubBase) stub).getUserData(ORIGIN_ELEMENT);
if (origin == null) return delegate.createMethod(stub);
return new ClsMethodImpl(stub) {
@NotNull
@Override
public PsiElement getNavigationElement() {
return origin;
}
};
}
@Override
public PsiModifierList createModifierList(PsiModifierListStub stub) {
return delegate.createModifierList(stub);
}
@Override
public PsiParameter createParameter(PsiParameterStub stub) {
return delegate.createParameter(stub);
}
@Override
public PsiParameterList createParameterList(PsiParameterListStub stub) {
return delegate.createParameterList(stub);
}
@Override
public PsiTypeParameter createTypeParameter(PsiTypeParameterStub stub) {
return delegate.createTypeParameter(stub);
}
@Override
public PsiTypeParameterList createTypeParameterList(PsiTypeParameterListStub stub) {
return delegate.createTypeParameterList(stub);
}
}
@@ -0,0 +1,212 @@
/*
* @author max
*/
package org.jetbrains.jet.asJava;
import com.intellij.openapi.compiler.ex.CompilerPathsEx;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.*;
import com.intellij.psi.*;
import com.intellij.psi.impl.file.PsiPackageImpl;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.codegen.JetTypeMapper;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
import org.jetbrains.jet.plugin.JetFileType;
import java.util.*;
public class JavaElementFinder extends PsiElementFinder {
private final Project project;
private final PsiManager psiManager;
private WeakHashMap<GlobalSearchScope, List<JetFile>> jetFiles = new WeakHashMap<GlobalSearchScope, List<JetFile>>();
public JavaElementFinder(Project project) {
this.project = project;
psiManager = PsiManager.getInstance(project);
// Monitoring for files instead of collecting them each time
VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileAdapter() {
@Override
public void fileCreated(VirtualFileEvent event) {
invalidateJetFilesCache();
}
@Override
public void fileDeleted(VirtualFileEvent event) {
invalidateJetFilesCache();
}
@Override
public void fileMoved(VirtualFileMoveEvent event) {
invalidateJetFilesCache();
}
@Override
public void fileCopied(VirtualFileCopyEvent event) {
invalidateJetFilesCache();
}
});
}
@Override
public PsiClass findClass(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) {
final PsiClass[] allClasses = findClasses(qualifiedName, scope);
return allClasses.length > 0 ? allClasses[0] : null;
}
@NotNull
@Override
public PsiClass[] findClasses(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) {
// Backend searches for java.lang.String. Will fail with SOE if continue
if (qualifiedName.startsWith("java.")) return PsiClass.EMPTY_ARRAY;
List<PsiClass> answer = new SmartList<PsiClass>();
final List<JetFile> filesInScope = collectProjectJetFiles(project, scope);
for (JetFile file : filesInScope) {
final String packageName = JetPsiUtil.getFQName(file);
if (packageName != null && qualifiedName.startsWith(packageName)) {
if (qualifiedName.equals(fqn(packageName, JvmAbi.PACKAGE_CLASS))) {
answer.add(new JetLightClass(psiManager, file, qualifiedName));
}
else {
for (JetDeclaration declaration : file.getDeclarations()) {
scanClasses(answer, declaration, qualifiedName, packageName, file);
}
}
}
}
return answer.toArray(new PsiClass[answer.size()]);
}
private void scanClasses(List<PsiClass> answer, JetDeclaration declaration, String qualifiedName, String containerFqn, JetFile file) {
if (declaration instanceof JetClassOrObject) {
String localName = getLocalName(declaration);
if (localName != null) {
String fqn = fqn(containerFqn, localName);
if (qualifiedName.equals(fqn)) {
answer.add(new JetLightClass(psiManager, file, qualifiedName));
}
else {
for (JetDeclaration child : ((JetClassOrObject) declaration).getDeclarations()) {
scanClasses(answer, child, qualifiedName, fqn, file);
}
}
}
}
else if (declaration instanceof JetClassObject) {
scanClasses(answer, ((JetClassObject) declaration).getObjectDeclaration(), qualifiedName, containerFqn, file);
}
}
private static String getLocalName(JetDeclaration declaration) {
String given = declaration.getName();
if (given != null) return given;
if (declaration instanceof JetObjectDeclaration) {
return JetTypeMapper.getLocalNameForObject((JetObjectDeclaration) declaration);
}
return null;
}
@Override
public Set<String> getClassNames(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope scope) {
Set<String> answer = new HashSet<String>();
String packageFQN = psiPackage.getQualifiedName();
for (JetFile psiFile : collectProjectJetFiles(project, GlobalSearchScope.allScope(project))) {
if (packageFQN.equals(JetPsiUtil.getFQName(psiFile))) {
answer.add(JvmAbi.PACKAGE_CLASS);
for (JetDeclaration declaration : psiFile.getDeclarations()) {
if (declaration instanceof JetClassOrObject) {
answer.add(getLocalName(declaration));
}
}
}
}
return answer;
}
private static String fqn(String packageName, String className) {
if (StringUtil.isEmpty(packageName)) return className;
return packageName + "." + className;
}
@Override
public PsiPackage findPackage(@NotNull String qualifiedName) {
final List<JetFile> psiFiles = collectProjectJetFiles(project, GlobalSearchScope.allScope(project));
for (JetFile psiFile : psiFiles) {
if (qualifiedName.equals(JetPsiUtil.getFQName(psiFile))) {
return new PsiPackageImpl(psiFile.getManager(), qualifiedName);
}
}
return null;
}
@NotNull
@Override
public PsiClass[] getClasses(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope scope) {
List<PsiClass> answer = new SmartList<PsiClass>();
final List<JetFile> filesInScope = collectProjectJetFiles(project, scope);
String packageFQN = psiPackage.getQualifiedName();
for (JetFile file : filesInScope) {
if (packageFQN.equals(JetPsiUtil.getFQName(file))) {
answer.add(new JetLightClass(psiManager, file, fqn(packageFQN, JvmAbi.PACKAGE_CLASS)));
for (JetDeclaration declaration : file.getDeclarations()) {
if (declaration instanceof JetClassOrObject) {
answer.add(new JetLightClass(psiManager, file, fqn(packageFQN, getLocalName(declaration))));
}
}
}
}
return answer.toArray(new PsiClass[answer.size()]);
}
private synchronized void invalidateJetFilesCache() {
jetFiles.clear();
}
private synchronized List<JetFile> collectProjectJetFiles(final Project project, @NotNull final GlobalSearchScope scope) {
List<JetFile> cachedFiles = jetFiles.get(scope);
if (cachedFiles == null) {
final List<JetFile> answer = new ArrayList<JetFile>();
final FileTypeManager fileTypeManager = FileTypeManager.getInstance();
VirtualFile[] contentRoots = ProjectRootManager.getInstance(project).getContentRoots();
CompilerPathsEx.visitFiles(contentRoots, new CompilerPathsEx.FileVisitor() {
@Override
protected void acceptFile(VirtualFile file, String fileRoot, String filePath) {
final FileType fileType = fileTypeManager.getFileTypeByFile(file);
if (fileType != JetFileType.INSTANCE) return;
if (scope.accept(file)) {
final PsiFile psiFile = psiManager.findFile(file);
if (psiFile instanceof JetFile) {
answer.add((JetFile) psiFile);
}
}
}
});
cachedFiles = answer;
jetFiles.put(scope, answer);
}
return cachedFiles;
}
}
@@ -0,0 +1,112 @@
/*
* @author max
*/
package org.jetbrains.jet.asJava;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiModificationTrackerImpl;
import com.intellij.psi.impl.PsiTreeChangeEventImpl;
import com.intellij.psi.impl.PsiTreeChangePreprocessor;
import com.intellij.psi.util.PsiModificationTracker;
import org.jetbrains.jet.lang.psi.JetBlockExpression;
import org.jetbrains.jet.lang.psi.JetClass;
import org.jetbrains.jet.lang.psi.JetFile;
public class JetCodeBlockModificationListener implements PsiTreeChangePreprocessor {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.jet.asJava.JetCodeBlockModificationListener");
private final PsiModificationTrackerImpl myModificationTracker;
public JetCodeBlockModificationListener(final PsiModificationTracker modificationTracker) {
myModificationTracker = (PsiModificationTrackerImpl) modificationTracker;
}
@Override
public void treeChanged(final PsiTreeChangeEventImpl event) {
switch (event.getCode()) {
case BEFORE_CHILDREN_CHANGE:
case BEFORE_PROPERTY_CHANGE:
case BEFORE_CHILD_MOVEMENT:
case BEFORE_CHILD_REPLACEMENT:
case BEFORE_CHILD_ADDITION:
case BEFORE_CHILD_REMOVAL:
break;
case CHILD_ADDED:
case CHILD_REMOVED:
case CHILD_REPLACED:
processChange(event.getParent(), event.getOldChild(), event.getChild());
break;
case CHILDREN_CHANGED:
// general childrenChanged() event after each change
if (!event.isGenericChildrenChange()) {
processChange(event.getParent(), event.getParent(), null);
}
break;
case CHILD_MOVED:
case PROPERTY_CHANGED:
myModificationTracker.incCounter();
break;
default:
LOG.error("Unknown code:" + event.getCode());
break;
}
}
private void processChange(final PsiElement parent, final PsiElement child1, final PsiElement child2) {
try {
if (!isInsideCodeBlock(parent)) {
if (parent != null && parent.getContainingFile() instanceof JetFile) {
myModificationTracker.incCounter();
}
else {
myModificationTracker.incOutOfCodeBlockModificationCounter();
}
return;
}
if (containsClassesInside(child1) || child2 != child1 && containsClassesInside(child2)) {
myModificationTracker.incCounter();
}
} catch (PsiInvalidElementAccessException e) {
myModificationTracker.incCounter(); // Shall not happen actually, just a pre-release paranoia
}
}
private static boolean containsClassesInside(final PsiElement element) {
if (element == null) return false;
if (element instanceof PsiClass) return true;
PsiElement child = element.getFirstChild();
while (child != null) {
if (containsClassesInside(child)) return true;
child = child.getNextSibling();
}
return false;
}
private static boolean isInsideCodeBlock(PsiElement element) {
if (element instanceof PsiFileSystemItem) {
return false;
}
if (element == null || element.getParent() == null) return true;
PsiElement parent = element;
while (true) {
if (parent instanceof PsiFile || parent instanceof PsiDirectory || parent == null) {
return false;
}
if (parent instanceof JetClass) return false; // anonymous or local class
if (parent instanceof JetBlockExpression) {
return true;
}
parent = parent.getParent();
}
}
}
@@ -0,0 +1,178 @@
/*
* @author max
*/
package org.jetbrains.jet.asJava;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Key;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiManagerImpl;
import com.intellij.psi.impl.compiled.ClsFileImpl;
import com.intellij.psi.impl.java.stubs.PsiClassStub;
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub;
import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl;
import com.intellij.psi.impl.light.AbstractLightClass;
import com.intellij.psi.stubs.PsiClassHolderFileStub;
import com.intellij.psi.stubs.StubElement;
import com.intellij.psi.util.CachedValue;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.util.containers.Stack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.codegen.ClassBuilder;
import org.jetbrains.jet.codegen.ClassBuilderFactory;
import org.jetbrains.jet.codegen.GenerationState;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.psi.JetPsiUtil;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.java.AnalyzerFacade;
import org.jetbrains.jet.lang.resolve.java.JetJavaMirrorMarker;
import org.jetbrains.jet.plugin.JetLanguage;
import java.util.Collections;
import java.util.List;
public class JetLightClass extends AbstractLightClass implements JetJavaMirrorMarker {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.jet.asJava.JetLightClass");
private final static Key<CachedValue<PsiJavaFileStub>> JAVA_API_STUB = Key.create("JAVA_API_STUB");
private final JetFile file;
private final String qualifiedName;
private PsiClass delegate;
public JetLightClass(PsiManager manager, JetFile file, String qualifiedName) {
super(manager, JetLanguage.INSTANCE);
this.file = file;
this.qualifiedName = qualifiedName;
}
@Override
public String getName() {
int idx = qualifiedName.lastIndexOf('.');
return idx > 0 ? qualifiedName.substring(idx + 1) : qualifiedName;
}
@Override
public PsiElement copy() {
return new JetLightClass(getManager(), file, qualifiedName);
}
@Override
public PsiFile getContainingFile() {
return file;
}
@Override
public PsiClass getDelegate() {
if (delegate == null) {
delegate = findClass(qualifiedName, getStub());
if (delegate == null) {
delegate = findClass(qualifiedName, getStub());
}
}
return delegate;
}
private static PsiClass findClass(String fqn, StubElement<?> stub) {
if (stub instanceof PsiClassStub && Comparing.equal(fqn, ((PsiClassStub) stub).getQualifiedName())) {
return (PsiClass) stub.getPsi();
}
for (StubElement child : stub.getChildrenStubs()) {
PsiClass answer = findClass(fqn, child);
if (answer != null) return answer;
}
return null;
}
@Override
public String getQualifiedName() {
return qualifiedName;
}
private PsiJavaFileStub getStub() {
CachedValue<PsiJavaFileStub> answer = file.getUserData(JAVA_API_STUB);
if (answer == null) {
answer = CachedValuesManager.getManager(getProject()).createCachedValue(new CachedValueProvider<PsiJavaFileStub>() {
@Override
public Result<PsiJavaFileStub> compute() {
return Result.create(calcStub(), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT);
}
}, false);
file.putUserData(JAVA_API_STUB, answer);
}
return answer.getValue();
}
private PsiJavaFileStub calcStub() {
final PsiJavaFileStubImpl answer = new PsiJavaFileStubImpl(JetPsiUtil.getFQName(file), true);
final Project project = getProject();
final Stack<StubElement> stubStack = new Stack<StubElement>();
final ClassBuilderFactory builderFactory = new ClassBuilderFactory() {
@Override
public ClassBuilder newClassBuilder() {
return new StubClassBuilder(stubStack);
}
@Override
public String asText(ClassBuilder builder) {
throw new UnsupportedOperationException("asText is not implemented"); // TODO
}
@Override
public byte[] asBytes(ClassBuilder builder) {
throw new UnsupportedOperationException("asBytes is not implemented"); // TODO
}
};
final GenerationState state = new GenerationState(project, builderFactory) {
@Override
protected void generateNamespace(JetFile namespace) {
PsiManager manager = PsiManager.getInstance(project);
stubStack.push(answer);
answer.setPsiFactory(new ClsWrapperStubPsiFactory());
final ClsFileImpl fakeFile = new ClsFileImpl((PsiManagerImpl) manager, new ClassFileViewProvider(manager, file.getVirtualFile())) {
@NotNull
@Override
public PsiClassHolderFileStub getStub() {
return answer;
}
};
fakeFile.setPhysical(false);
answer.setPsi(fakeFile);
try {
super.generateNamespace(namespace);
}
finally {
final StubElement pop = stubStack.pop();
if (pop != answer) {
LOG.error("Unbalanced stack operations: " + pop);
}
}
}
};
List<JetFile> files = Collections.singletonList(file);
final BindingContext context = AnalyzerFacade.shallowAnalyzeFiles(files);
state.compileCorrectFiles(context, files);
state.getFactory().files();
return answer;
}
@Override
public boolean isEquivalentTo(PsiElement another) {
return another instanceof PsiClass && Comparing.equal(((PsiClass) another).getQualifiedName(), getQualifiedName());
}
}
@@ -0,0 +1,99 @@
/*
* @author max
*/
package org.jetbrains.jet.asJava;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.compiled.InnerClassSourceStrategy;
import com.intellij.psi.impl.compiled.StubBuildingVisitor;
import com.intellij.psi.stubs.StubBase;
import com.intellij.psi.stubs.StubElement;
import com.intellij.util.containers.Stack;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.codegen.ClassBuilder;
import org.jetbrains.jet.lang.resolve.java.JvmAbi;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import java.util.List;
public class StubClassBuilder extends ClassBuilder {
private static final InnerClassSourceStrategy<Object> EMPTY_STRATEGY = new InnerClassSourceStrategy<Object>() {
@Override
public Object findInnerClass(String s, Object o) {
return null;
}
@Override
public ClassReader readerForInnerClass(Object o) {
throw new UnsupportedOperationException("Shall not be called!");
}
};
private final StubElement parent;
private StubBuildingVisitor v;
private final Stack<StubElement> parentStack;
private boolean isNamespace = false;
public StubClassBuilder(Stack<StubElement> parentStack) {
this.parentStack = parentStack;
this.parent = parentStack.peek();
}
@Override
public ClassVisitor getVisitor() {
assert v != null : "Called before class is defined";
return v;
}
@Override
public void defineClass(PsiElement origin, int version, int access, String name, @Nullable String signature, String superName, String[] interfaces) {
assert v == null : "defineClass() called twice?";
v = new StubBuildingVisitor<Object>(null, EMPTY_STRATEGY, parent, access);
super.defineClass(origin, version, access, name, signature, superName, interfaces);
if (name.equals(JvmAbi.PACKAGE_CLASS) || name.endsWith("/" + JvmAbi.PACKAGE_CLASS)) {
isNamespace = true;
}
else {
parentStack.push(v.getResult());
}
((StubBase) v.getResult()).putUserData(ClsWrapperStubPsiFactory.ORIGIN_ELEMENT, origin);
}
@Override
public MethodVisitor newMethod(@Nullable PsiElement origin, int access, String name, String desc, @Nullable String signature, @Nullable String[] exceptions) {
final MethodVisitor answer = super.newMethod(origin, access, name, desc, signature, exceptions);
markLastChild(origin);
return answer;
}
@Override
public FieldVisitor newField(@Nullable PsiElement origin, int access, String name, String desc, @Nullable String signature, @Nullable Object value) {
final FieldVisitor answer = super.newField(origin, access, name, desc, signature, value);
markLastChild(origin);
return answer;
}
private void markLastChild(PsiElement origin) {
final List children = v.getResult().getChildrenStubs();
StubBase last = (StubBase) children.get(children.size() - 1);
last.putUserData(ClsWrapperStubPsiFactory.ORIGIN_ELEMENT, origin);
}
@Override
public void done() {
if (!isNamespace) {
final StubElement pop = parentStack.pop();
assert pop == v.getResult();
}
super.done();
}
@Override
public boolean generateCode() {
return false;
}
}