From a6ec212d04f20d6ca44439b88ee032b39643cf47 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Sat, 8 Oct 2016 12:38:28 +0300 Subject: [PATCH] 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 --- .../CompilerReferenceServiceImpl.java | 124 +++++++++++++++--- .../compiler/server/BuildManagerListener.java | 12 +- 2 files changed, 109 insertions(+), 27 deletions(-) diff --git a/java/compiler/impl/src/com/intellij/compiler/CompilerReferenceServiceImpl.java b/java/compiler/impl/src/com/intellij/compiler/CompilerReferenceServiceImpl.java index c70e43b10063..f7c19b92563b 100644 --- a/java/compiler/impl/src/com/intellij/compiler/CompilerReferenceServiceImpl.java +++ b/java/compiler/impl/src/com/intellij/compiler/CompilerReferenceServiceImpl.java @@ -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 myChangedModules = ContainerUtil.newConcurrentSet(); private final Set 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 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 myChangedModules = ContainerUtil.newHashSet(); + private final Set 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 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); + } + } + } } diff --git a/java/compiler/impl/src/com/intellij/compiler/server/BuildManagerListener.java b/java/compiler/impl/src/com/intellij/compiler/server/BuildManagerListener.java index 2e5b6fbf3ca0..44b61bb6e80a 100644 --- a/java/compiler/impl/src/com/intellij/compiler/server/BuildManagerListener.java +++ b/java/compiler/impl/src/com/intellij/compiler/server/BuildManagerListener.java @@ -11,10 +11,10 @@ import java.util.UUID; */ public interface BuildManagerListener { Topic 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) {} }