IDEA-137521 GDSL activation state fix & refactoring

This commit is contained in:
Daniil Ovchinnikov
2015-03-30 19:12:10 +03:00
parent 1649fefd93
commit ce6b623e69
5 changed files with 163 additions and 112 deletions
@@ -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<Element> {
private final Map<VirtualFile, String> myStatus = new THashMap<VirtualFile, String>();
private static final String ENABLED = "enabled";
public class DslActivationStatus implements PersistentStateComponent<DslActivationStatus.State> {
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<Entry> entries = ContainerUtil.newArrayList();
}
private final Map<VirtualFile, Entry> myStatus = Collections.synchronizedMap(new FactoryMap<VirtualFile, Entry>() {
@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<VirtualFile, String> 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<Element> 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);
}
}
}
}
@@ -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<GroovyClassDescriptor> INITIAL_CONTEXT = Key.create("gdsl.initialContext");
public static final Condition<VirtualFile> GDSL_FILTER = new Condition<VirtualFile>() {
@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() {
@@ -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
@@ -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<String> {
private static final Key<Pair<GroovyDslExecutor, Long>> CACHED_EXECUTOR = Key.create("CachedGdslExecutor");
private static final Logger LOG = Logger.getInstance(GroovyDslFileIndex.class);
@NonNls public static final ID<String, Void> 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<Pair<GroovyDslExecutor, Long>> CACHED_EXECUTOR = Key.create("CachedGdslExecutor");
private static final Key<CachedValue<List<GroovyDslScript>>> 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<String, Void> NAME = ID.create("GroovyDslFileIndex");
private static final MultiMap<String, LinkedBlockingQueue<Pair<VirtualFile, GroovyDslExecutor>>> filesInProcessing =
new ConcurrentMultiMap<String, LinkedBlockingQueue<Pair<VirtualFile, GroovyDslExecutor>>>();
private static final ThreadPoolExecutor ourPool = new ThreadPoolExecutor(4, 4, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), ConcurrencyUtil.newNamedThreadFactory("Groovy DSL File Index Executor"));
private static final ThreadPoolExecutor ourPool = new ThreadPoolExecutor(
4, 4, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), 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<String> {
@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<String> {
}, 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<String> {
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<String> {
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<CachedValue<List<GroovyDslScript>>> SCRIPTS_CACHE = Key.create("GdslScriptCache");
private static List<VirtualFile> getGdslFiles(final Project project) {
List<VirtualFile> result = ContainerUtil.newArrayList();
return CachedValuesManager.getManager(project).getCachedValue(project, new CachedValueProvider<List<VirtualFile>>() {
@Nullable
@Override
public Result<List<VirtualFile>> compute() {
final List<VirtualFile> result = ContainerUtil.newArrayList();
result.addAll(getBundledGdslFiles());
result.addAll(getProjectGdslFiles(project));
return Result.create(result, PsiModificationTracker.MODIFICATION_COUNT, ProjectRootManager.getInstance(project));
}
});
}
private static List<VirtualFile> getBundledGdslFiles() {
final List<VirtualFile> result = ContainerUtil.newArrayList();
for (File file : getBundledScriptFolders()) {
if (file.exists()) {
File[] children = file.listFiles();
@@ -316,7 +339,11 @@ public class GroovyDslFileIndex extends ScalarIndexExtension<String> {
}
}
}
return result;
}
private static List<VirtualFile> getProjectGdslFiles(Project project) {
final List<VirtualFile> 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<String> {
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<String> {
return result;
}
@NotNull
private static Set<File> getBundledScriptFolders() {
final GroovyFrameworkConfigNotification[] extensions = GroovyFrameworkConfigNotification.EP_NAME.getExtensions();
@@ -436,7 +464,7 @@ public class GroovyDslFileIndex extends ScalarIndexExtension<String> {
@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<String> {
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<String> {
//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);
}
}
@@ -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);
}
}