mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-137521 GDSL activation state fix & refactoring
This commit is contained in:
+70
-61
@@ -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() {
|
||||
|
||||
+27
-19
@@ -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
|
||||
|
||||
+54
-30
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user