CompilerReferenceServiceImpl:

* mark modules compiled with errors as dirty
 * restore state after partial compilation was done (e.g. module test run)
 * mark as dirty modules changed during compilation
This commit is contained in:
Dmitry Batkovich
2016-10-08 13:02:29 +03:00
parent 5a90c7ac89
commit a6ec212d04
2 changed files with 109 additions and 27 deletions
@@ -17,12 +17,15 @@ package com.intellij.compiler;
import com.intellij.compiler.server.BuildManagerListener;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.openapi.compiler.*;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.ModificationTracker;
import com.intellij.openapi.util.UserDataHolderBase;
import com.intellij.openapi.vfs.*;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
@@ -43,44 +46,69 @@ import java.util.Collections;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.LongAdder;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.intellij.psi.search.GlobalSearchScope.*;
public class CompilerReferenceServiceImpl extends CompilerReferenceService implements ModificationTracker {
private final ProjectFileIndex myProjectFileIndex;
private final Set<Module> myChangedModules = ContainerUtil.newConcurrentSet();
private final Set<FileType> myFileTypes;
private final DirtyModulesHolder myDirtyModulesHolder;
private final ProjectFileIndex myProjectFileIndex;
private final LongAdder myCompilationCount = new LongAdder();
private volatile CompilerReferenceReader myReader;
private volatile GlobalSearchScope myDirtyScope = EMPTY_SCOPE;
private final Object myLock = new Object();
public CompilerReferenceServiceImpl(Project project) {
super(project);
myProjectFileIndex = ProjectRootManager.getInstance(project).getFileIndex();
myDirtyModulesHolder = new DirtyModulesHolder();
myFileTypes = Collections.unmodifiableSet(ContainerUtil.set(JavaFileType.INSTANCE));
myProjectFileIndex = ProjectRootManager.getInstance(project).getFileIndex();
}
@Override
public void projectOpened() {
if (isEnabled()) {
myDirtyModulesHolder.markAsDirty(ModuleManager.getInstance(myProject).getModules());
myProject.getMessageBus().connect(myProject).subscribe(BuildManagerListener.TOPIC, new BuildManagerListener() {
@Override
public void beforeBuildProcessStarted(Project project, UUID sessionId) {
}
@Override
public void buildStarted(Project project, UUID sessionId, boolean isAutomake) {
myDirtyModulesHolder.compilationPhaseStarted();
closeReaderIfNeed();
}
});
CompilerManager.getInstance(myProject).addCompilationStatusListener(new CompilationStatusListener() {
@Override
public void compilationFinished(boolean aborted, int errors, int warnings, CompileContext compileContext) {
compilationFinished(errors, compileContext);
}
@Override
public void buildFinished(Project project, UUID sessionId, boolean isAutomake) {
myChangedModules.clear();
myDirtyScope = EMPTY_SCOPE;
public void automakeCompilationFinished(int errors, int warnings, CompileContext compileContext) {
compilationFinished(errors, compileContext);
}
private void compilationFinished(int errors, CompileContext context) {
final Module[] compilationModules = context.getCompileScope().getAffectedModules();
final Set<Module> modulesWithErrors;
if (errors != 0) {
modulesWithErrors = Stream
.of(context.getMessages(CompilerMessageCategory.ERROR))
.map(CompilerMessage::getVirtualFile)
.distinct()
.map(myProjectFileIndex::getModuleForFile)
.collect(Collectors.toSet());
}
else {
modulesWithErrors = Collections.emptySet();
}
myCompilationCount.increment();
myDirtyModulesHolder.compilationPhaseFinished(compilationModules, modulesWithErrors);
openReaderIfNeed();
}
});
@@ -124,14 +152,7 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple
}
private void processChange(VirtualFile file) {
if (myReader != null && myProjectFileIndex.isInSourceContent(file) && myFileTypes.contains(file.getFileType())) {
final Module module = myProjectFileIndex.getModuleForFile(file);
if (module != null) {
if (myChangedModules.add(module)) {
myDirtyScope = myDirtyScope.union(module.getModuleWithDependentsScope());
}
}
}
myDirtyModulesHolder.fileChanged(file);
}
}, myProject);
}
@@ -168,7 +189,7 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple
TIntHashSet referentFileIds = getReferentFileIds(element, adapter);
if (referentFileIds == null) return null;
return getScopeRestrictedByFileTypes(new ScopeWithoutReferencesOnCompilation(referentFileIds).intersectWith(notScope(myDirtyScope)),
return getScopeRestrictedByFileTypes(new ScopeWithoutReferencesOnCompilation(referentFileIds).intersectWith(notScope(myDirtyModulesHolder.getDirtyScope())),
myFileTypes.toArray(new FileType[myFileTypes.size()]));
}
@@ -184,7 +205,7 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple
return null;
}
if (myDirtyScope.contains(vFile)) {
if (myDirtyModulesHolder.contains(vFile)) {
return null;
}
CompilerElement[] compilerElements;
@@ -283,4 +304,65 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple
public long getModificationCount() {
return myCompilationCount.longValue();
}
private class DirtyModulesHolder extends UserDataHolderBase {
private final Set<Module> myChangedModules = ContainerUtil.newHashSet();
private final Set<Module> myChangedModulesDuringCompilation = ContainerUtil.newHashSet();
private boolean myCompilationPhase;
private final Object myLock = new Object();
private void compilationPhaseStarted() {
synchronized (myLock) {
myCompilationPhase = true;
}
}
private void compilationPhaseFinished(Module[] compilationModules, Set<Module> modulesWithErrors) {
synchronized (myLock) {
myCompilationPhase = false;
ContainerUtil.removeAll(myChangedModules, compilationModules);
myChangedModules.addAll(modulesWithErrors);
myChangedModules.addAll(ContainerUtil.newHashSet(myChangedModulesDuringCompilation));
myChangedModulesDuringCompilation.clear();
}
}
private GlobalSearchScope getDirtyScope() {
return CachedValuesManager.getManager(myProject).getCachedValue(this, () -> {
synchronized (myLock) {
final GlobalSearchScope dirtyScope =
myChangedModules.stream().map(Module::getModuleWithDependentsScope).reduce(EMPTY_SCOPE, (s1, s2) -> s1.union(s2));
return CachedValueProvider.Result.create(dirtyScope, PsiModificationTracker.MODIFICATION_COUNT, CompilerReferenceServiceImpl.this);
}
});
}
private void fileChanged(VirtualFile file) {
if (myProjectFileIndex.isInSourceContent(file) && myFileTypes.contains(file.getFileType())) {
final Module module = myProjectFileIndex.getModuleForFile(file);
if (module != null) {
synchronized (myLock) {
if (myCompilationPhase) {
myChangedModulesDuringCompilation.add(module);
} else {
myChangedModules.add(module);
}
}
}
}
}
private boolean contains(VirtualFile file) {
return getDirtyScope().contains(file);
}
private void markAsDirty(Module[] modules) {
//TODO delete; the service should be available on IDE restart
synchronized (myLock) {
Collections.addAll(myChangedModules, modules);
}
}
}
}
@@ -11,10 +11,10 @@ import java.util.UUID;
*/
public interface BuildManagerListener {
Topic<BuildManagerListener> TOPIC = Topic.create("Build Manager", BuildManagerListener.class);
void beforeBuildProcessStarted(Project project, UUID sessionId);
void buildStarted(Project project, UUID sessionId, boolean isAutomake);
void buildFinished(Project project, UUID sessionId, boolean isAutomake);
default void beforeBuildProcessStarted(Project project, UUID sessionId) {}
default void buildStarted(Project project, UUID sessionId, boolean isAutomake) {}
default void buildFinished(Project project, UUID sessionId, boolean isAutomake) {}
}