From ce6b623e692902afbf4b22b744de39a69ca125f7 Mon Sep 17 00:00:00 2001 From: Daniil Ovchinnikov Date: Mon, 30 Mar 2015 18:47:04 +0300 Subject: [PATCH] IDEA-137521 GDSL activation state fix & refactoring --- .../groovy/dsl/DslActivationStatus.java | 131 ++++++++++-------- .../plugins/groovy/dsl/GdslUtil.java | 11 ++ .../groovy/dsl/GroovyDslAnnotator.java | 46 +++--- .../groovy/dsl/GroovyDslFileIndex.java | 84 +++++++---- .../groovy/dsl/DslErrorReporterImpl.java | 3 +- 5 files changed, 163 insertions(+), 112 deletions(-) diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/dsl/DslActivationStatus.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/dsl/DslActivationStatus.java index 267707e08207..981cb46de007 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/dsl/DslActivationStatus.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/dsl/DslActivationStatus.java @@ -16,79 +16,88 @@ package org.jetbrains.plugins.groovy.dsl; import com.intellij.openapi.components.*; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; -import gnu.trove.THashMap; -import org.jdom.Element; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.FactoryMap; +import com.intellij.util.xmlb.annotations.AbstractCollection; +import com.intellij.util.xmlb.annotations.Attribute; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.List; +import java.util.Collection; +import java.util.Collections; import java.util.Map; @State( name = "DslActivationStatus", - storages = @Storage(file = StoragePathMacros.APP_CONFIG + "/dslActivation.xml", roamingType = RoamingType.DISABLED) + storages = @Storage(file = StoragePathMacros.APP_CONFIG + "/dslActivationStatus.xml") ) -public class DslActivationStatus implements PersistentStateComponent { - private final Map myStatus = new THashMap(); - private static final String ENABLED = "enabled"; +public class DslActivationStatus implements PersistentStateComponent { + + enum Status { + ACTIVE, + MODIFIED, + ERROR + } + + public static class Entry { + @Attribute + public String url; + @Attribute + public Status status; + @Attribute + public String message; + + public Entry() { + } + + public Entry(String url, Status status, String message) { + this.url = url; + this.status = status; + this.message = message; + } + } + + public static class State { + @AbstractCollection(surroundWithTag = false) + public Collection entries = ContainerUtil.newArrayList(); + } + + private final Map myStatus = Collections.synchronizedMap(new FactoryMap() { + @Nullable + @Override + protected DslActivationStatus.Entry create(VirtualFile key) { + return new DslActivationStatus.Entry(key.getUrl(), Status.ACTIVE, null); + } + }); + + public Entry get(@NotNull VirtualFile file) { + return myStatus.get(file); + } + + @Nullable + @Override + public State getState() { + final State state = new State(); + state.entries = myStatus.values(); + return state; + } + + @Override + public void loadState(State state) { + final VirtualFileManager fileManager = VirtualFileManager.getInstance(); + if (state.entries == null) return; + for (Entry entry : state.entries) { + if (entry.url == null || entry.status == null) continue; + final VirtualFile file = fileManager.findFileByUrl(entry.url); + if (file != null) { + myStatus.put(file, entry); + } + } + } public static DslActivationStatus getInstance() { return ServiceManager.getService(DslActivationStatus.class); } - - public synchronized void activateUntilModification(@NotNull VirtualFile vfile) { - myStatus.put(vfile, ENABLED); - } - - public synchronized void disableFile(@NotNull VirtualFile vfile, @NotNull String error) { - myStatus.put(vfile, error); - } - - @Nullable - public synchronized String getInactivityReason(VirtualFile file) { - String status = myStatus.get(file); - return ENABLED.equals(status) ? null : status; - } - - public synchronized boolean isActivated(VirtualFile file) { - final String status = myStatus.get(file); - if (status == null) { - myStatus.put(file, ENABLED); - return true; - } - return ENABLED.equals(status); - } - - @Nullable - @Override - public synchronized Element getState() { - Element root = new Element("x"); - for (Map.Entry entry : myStatus.entrySet()) { - VirtualFile file = entry.getKey(); - String status = entry.getValue(); - Element element = new Element("file"); - root.addContent(element); - element.setAttribute("url", file.getUrl()); - if (!ENABLED.equals(status)) { - element.setAttribute("status", status); - } - } - return root; - } - - @Override - public synchronized void loadState(Element state) { - List children = state.getChildren("file"); - for (Element element : children) { - String url = element.getAttributeValue("url", ""); - String status = element.getAttributeValue("status"); - VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(url); - if (file != null) { - myStatus.put(file, StringUtil.isNotEmpty(status) ? status : ENABLED); - } - } - } } diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/dsl/GdslUtil.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/dsl/GdslUtil.java index 6afe58b98efa..db9c706e5d3e 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/dsl/GdslUtil.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/dsl/GdslUtil.java @@ -15,10 +15,21 @@ */ package org.jetbrains.plugins.groovy.dsl; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Key; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.Contract; public class GdslUtil { public static final Key INITIAL_CONTEXT = Key.create("gdsl.initialContext"); + + public static final Condition GDSL_FILTER = new Condition() { + @Override + @Contract("null -> false") + public boolean value(VirtualFile file) { + return file != null && !file.isDirectory() && "gdsl".equals(file.getExtension()); + } + }; static volatile boolean ourGdslStopped = false; static void stopGdsl() { diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/dsl/GroovyDslAnnotator.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/dsl/GroovyDslAnnotator.java index 72ba86b51f1c..80cb00a54471 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/dsl/GroovyDslAnnotator.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/dsl/GroovyDslAnnotator.java @@ -31,6 +31,9 @@ import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.codeInspection.GroovyQuickFixFactory; import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; +import org.jetbrains.plugins.groovy.util.GrFileIndexUtil; + +import static org.jetbrains.plugins.groovy.dsl.DslActivationStatus.Status.*; /** * @author peter @@ -39,26 +42,30 @@ public class GroovyDslAnnotator implements Annotator, DumbAware { @Override public void annotate(@NotNull PsiElement psiElement, @NotNull AnnotationHolder holder) { - if (psiElement instanceof GroovyFile) { - final VirtualFile vfile = ((GroovyFile)psiElement).getVirtualFile(); - if (vfile != null && "gdsl".equals(vfile.getExtension()) && - (!GroovyDslFileIndex.isActivated(vfile) || FileDocumentManager.getInstance().isFileModified(vfile))) { - final String reason = GroovyDslFileIndex.getInactivityReason(vfile); - final String message; - boolean modified = reason == null || GroovyDslFileIndex.MODIFIED.equals(reason); - if (modified) { - message = "DSL descriptor file has been changed and isn't currently executed."; - } else { - message = "DSL descriptor file has been disabled due to a processing error."; - } - final Annotation annotation = holder.createWarningAnnotation(psiElement, message); - annotation.setFileLevelAnnotation(true); - if (!modified) { - annotation.registerFix(GroovyQuickFixFactory.getInstance().createInvestigateFix(reason)); - } - annotation.registerFix(new ActivateFix(vfile)); + if (!(psiElement instanceof GroovyFile)) return; + + final GroovyFile groovyFile = (GroovyFile)psiElement; + if (!GrFileIndexUtil.isGroovySourceFile(groovyFile)) return; + + final VirtualFile vfile = groovyFile.getVirtualFile(); + if (!GdslUtil.GDSL_FILTER.value(vfile)) return; + + final DslActivationStatus.Status status = GroovyDslFileIndex.getStatus(vfile); + if (status == ACTIVE) return; + + final String message = status == MODIFIED + ? "DSL descriptor file has been changed and isn't currently executed." + : "DSL descriptor file has been disabled due to a processing error."; + + final Annotation annotation = holder.createWarningAnnotation(psiElement, message); + annotation.setFileLevelAnnotation(true); + if (status == ERROR) { + final String error = GroovyDslFileIndex.getInactivityReason(vfile); + if (error != null) { + annotation.registerFix(GroovyQuickFixFactory.getInstance().createInvestigateFix(error)); } } + annotation.registerFix(new ActivateFix(vfile)); } private static class ActivateFix implements IntentionAction { @@ -77,7 +84,8 @@ public class GroovyDslAnnotator implements Annotator, DumbAware { @Override @NotNull public String getFamilyName() { - return "Activate DSL descriptor"; + //noinspection DialogTitleCapitalization + return "Activate DSL Descriptor"; } @Override diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/dsl/GroovyDslFileIndex.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/dsl/GroovyDslFileIndex.java index 7fa143f1501b..5602c25f5e61 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/dsl/GroovyDslFileIndex.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/dsl/GroovyDslFileIndex.java @@ -30,7 +30,6 @@ import com.intellij.openapi.util.ModificationTracker; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Trinity; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileAdapter; import com.intellij.openapi.vfs.VirtualFileEvent; @@ -59,6 +58,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.GroovyFileType; import org.jetbrains.plugins.groovy.annotator.GroovyFrameworkConfigNotification; +import org.jetbrains.plugins.groovy.dsl.DslActivationStatus.Status; import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; @@ -76,35 +76,35 @@ import java.util.regex.Pattern; * @author peter */ public class GroovyDslFileIndex extends ScalarIndexExtension { - private static final Key> CACHED_EXECUTOR = Key.create("CachedGdslExecutor"); - private static final Logger LOG = Logger.getInstance(GroovyDslFileIndex.class); - @NonNls public static final ID NAME = ID.create("GroovyDslFileIndex"); - @NonNls private static final String OUR_KEY = "ourKey"; - public static final String MODIFIED = "Modified"; - private final MyDataIndexer myDataIndexer = new MyDataIndexer(); + private static final Key> CACHED_EXECUTOR = Key.create("CachedGdslExecutor"); + private static final Key>> SCRIPTS_CACHE = Key.create("GdslScriptCache"); + private static final Logger LOG = Logger.getInstance(GroovyDslFileIndex.class); + private static final @NonNls String OUR_KEY = "ourKey"; + public static final @NonNls ID NAME = ID.create("GroovyDslFileIndex"); private static final MultiMap>> filesInProcessing = new ConcurrentMultiMap>>(); - private static final ThreadPoolExecutor ourPool = new ThreadPoolExecutor(4, 4, 1, TimeUnit.SECONDS, new LinkedBlockingQueue(), ConcurrencyUtil.newNamedThreadFactory("Groovy DSL File Index Executor")); + private static final ThreadPoolExecutor ourPool = new ThreadPoolExecutor( + 4, 4, 1, TimeUnit.SECONDS, new LinkedBlockingQueue(), ConcurrencyUtil.newNamedThreadFactory("Groovy DSL File Index Executor") + ); static { ourPool.allowCoreThreadTimeOut(true); } + private final MyDataIndexer myDataIndexer = new MyDataIndexer(); private final EnumeratorStringDescriptor myKeyDescriptor = new EnumeratorStringDescriptor(); public GroovyDslFileIndex() { VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileAdapter() { - @Override public void contentsChanged(@NotNull VirtualFileEvent event) { - if (event.getFileName().endsWith(".gdsl")) { - disableFile(event.getFile(), MODIFIED); - } + final VirtualFile file = event.getFile(); + if (event.isFromRefresh() || !GdslUtil.GDSL_FILTER.value(file) || getStatus(file) != Status.ACTIVE) return; + disableFile(file, Status.MODIFIED, null); } - }); } @@ -144,18 +144,22 @@ public class GroovyDslFileIndex extends ScalarIndexExtension { @Nullable public static String getInactivityReason(VirtualFile file) { - return DslActivationStatus.getInstance().getInactivityReason(file); + return DslActivationStatus.getInstance().get(file).message; } public static boolean isActivated(VirtualFile file) { - return DslActivationStatus.getInstance().isActivated(file); + return DslActivationStatus.getInstance().get(file).status == Status.ACTIVE; } public static void activateUntilModification(final VirtualFile vfile) { - DslActivationStatus.getInstance().activateUntilModification(vfile); + setStatusAndMessage(vfile, Status.ACTIVE, null); clearScriptCache(); } + public static Status getStatus(final VirtualFile file) { + return DslActivationStatus.getInstance().get(file).status; + } + private static void clearScriptCache() { Application app = ApplicationManager.getApplication(); app.invokeLater(new Runnable() { @@ -169,12 +173,18 @@ public class GroovyDslFileIndex extends ScalarIndexExtension { }, app.getDisposed()); } - static void disableFile(final VirtualFile vfile, String error) { - DslActivationStatus.getInstance().disableFile(vfile, error); + static void disableFile(@NotNull VirtualFile vfile, @NotNull Status status, @Nullable String message) { + assert status != Status.ACTIVE; + setStatusAndMessage(vfile, status, message); vfile.putUserData(CACHED_EXECUTOR, null); clearScriptCache(); } + private static void setStatusAndMessage(@NotNull VirtualFile vfile, @NotNull Status status, @Nullable String message) { + final DslActivationStatus.Entry entry = DslActivationStatus.getInstance().get(vfile); + entry.status = status; + entry.message = message; + } @Nullable private static GroovyDslExecutor getCachedExecutor(@NotNull final VirtualFile file, final long stamp) { @@ -198,6 +208,7 @@ public class GroovyDslFileIndex extends ScalarIndexExtension { final Project project = scriptFile.getProject(); for (GroovyDslScript script : getDslScripts(project)) { final MultiMap staticInfo = script.getStaticInfo(); + //noinspection unchecked final Collection infos = staticInfo != null ? staticInfo.get("scriptSuperClass") : Collections.emptyList(); for (Object info : infos) { @@ -290,17 +301,29 @@ public class GroovyDslFileIndex extends ScalarIndexExtension { if (place instanceof PsiAnnotation) return true; if (place instanceof GrClosableBlock || place instanceof GrTypeDefinition || - place instanceof PsiFile) return false; + place instanceof PsiFile) { + return false; + } place = place.getParent(); } return false; } - private static final Key>> SCRIPTS_CACHE = Key.create("GdslScriptCache"); - private static List getGdslFiles(final Project project) { - List result = ContainerUtil.newArrayList(); + return CachedValuesManager.getManager(project).getCachedValue(project, new CachedValueProvider>() { + @Nullable + @Override + public Result> compute() { + final List result = ContainerUtil.newArrayList(); + result.addAll(getBundledGdslFiles()); + result.addAll(getProjectGdslFiles(project)); + return Result.create(result, PsiModificationTracker.MODIFICATION_COUNT, ProjectRootManager.getInstance(project)); + } + }); + } + private static List getBundledGdslFiles() { + final List result = ContainerUtil.newArrayList(); for (File file : getBundledScriptFolders()) { if (file.exists()) { File[] children = file.listFiles(); @@ -316,7 +339,11 @@ public class GroovyDslFileIndex extends ScalarIndexExtension { } } } + return result; + } + private static List getProjectGdslFiles(Project project) { + final List result = ContainerUtil.newArrayList(); final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex(); final GlobalSearchScope scope = GlobalSearchScope.allScope(project); @@ -324,7 +351,7 @@ public class GroovyDslFileIndex extends ScalarIndexExtension { if (!vfile.isValid()) { continue; } - if (vfile.isDirectory() || !vfile.getName().endsWith(".gdsl")) { + if (!GdslUtil.GDSL_FILTER.value(vfile)) { LOG.error("Index returned non-gdsl file: " + vfile); continue; } @@ -342,6 +369,7 @@ public class GroovyDslFileIndex extends ScalarIndexExtension { return result; } + @NotNull private static Set getBundledScriptFolders() { final GroovyFrameworkConfigNotification[] extensions = GroovyFrameworkConfigNotification.EP_NAME.getExtensions(); @@ -436,7 +464,7 @@ public class GroovyDslFileIndex extends ScalarIndexExtension { @Override public boolean acceptInput(@NotNull final VirtualFile file) { - return StringUtil.endsWith(file.getNameSequence(), ".gdsl"); + return GdslUtil.GDSL_FILTER.value(file); } } @@ -504,7 +532,7 @@ public class GroovyDslFileIndex extends ScalarIndexExtension { LOG.error(e); return null; } - invokeDslErrorPopup(e, project, vfile); + DslErrorReporter.getInstance().invokeDslErrorPopup(e, project, vfile); //noinspection InstanceofCatchParameter if (e instanceof OutOfMemoryError) { @@ -514,14 +542,10 @@ public class GroovyDslFileIndex extends ScalarIndexExtension { //noinspection InstanceofCatchParameter if (e instanceof NoClassDefFoundError) { GdslUtil.stopGdsl(); - throw (NoClassDefFoundError) e; + throw (NoClassDefFoundError)e; } return null; } } - static void invokeDslErrorPopup(Throwable e, final Project project, @NotNull VirtualFile vfile) { - DslErrorReporter.getInstance().invokeDslErrorPopup(e, project, vfile); - } - } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/DslErrorReporterImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/DslErrorReporterImpl.java index c61e9c304b7f..5355a6375f6c 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/DslErrorReporterImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/DslErrorReporterImpl.java @@ -43,7 +43,7 @@ public class DslErrorReporterImpl extends DslErrorReporter { final String exceptionText = ExceptionUtil.getThrowableText(e); LOG.info(exceptionText); - GroovyDslFileIndex.disableFile(vfile, exceptionText); + GroovyDslFileIndex.disableFile(vfile, DslActivationStatus.Status.ERROR, exceptionText); if (!ApplicationManagerEx.getApplicationEx().isInternal() && !ProjectRootManager.getInstance(project).getFileIndex().isInContent(vfile)) { @@ -60,6 +60,5 @@ public class DslErrorReporterImpl extends DslErrorReporter { notification.expire(); } }).notify(project); - } }