IDEA-CR-64585: IDEA-245071: implement DaemonCodeAnalyzerImpl#serializeCodeInsightPasses()

GitOrigin-RevId: 077ab6dc38924188f1017543763611915160f7ca
This commit is contained in:
Vladimir Dolzhenko
2020-07-18 20:03:04 +00:00
committed by intellij-monorepo-bot
parent 9f338cae80
commit fe2da78da7
6 changed files with 130 additions and 49 deletions
@@ -2122,7 +2122,7 @@ public class DaemonRespondToChangesTest extends DaemonAnalyzerTestCase {
}
public void testAddRemoveHighlighterRaceInIncorrectAnnotatorsWhichUseFileRecursiveVisit() {
useAnnotatorsIn(JavaFileType.INSTANCE.getLanguage(), new MyRecordingAnnotator[]{new MyIncorrectlyRecursiveAnnotator()}, () -> {
useAnnotatorsIn(JavaFileType.INSTANCE.getLanguage(), new MyIncorrectlyRecursiveAnnotator(), () -> {
@Language("JAVA")
String text1 = "class X {\n" +
" int foo(Object param) {\n" +
@@ -2148,28 +2148,59 @@ public class DaemonRespondToChangesTest extends DaemonAnalyzerTestCase {
public static void useAnnotatorsIn(@NotNull com.intellij.lang.Language language,
MyRecordingAnnotator @NotNull [] annotators,
@NotNull Runnable runnable) {
useAnnotatorsIn(Collections.singletonMap(language, annotators), runnable);
}
public static void useAnnotatorsIn(@NotNull com.intellij.lang.Language language,
@NotNull MyRecordingAnnotator annotator,
@NotNull Runnable runnable) {
useAnnotatorsIn(Collections.singletonMap(language, new MyRecordingAnnotator[]{annotator}), runnable);
}
public static void useAnnotatorsIn(@NotNull Map<com.intellij.lang.Language, MyRecordingAnnotator @NotNull []> annotatorsByLanguage,
@NotNull Runnable runnable) {
MyRecordingAnnotator.clearAll();
for (Annotator annotator : annotators) {
LanguageAnnotators.INSTANCE.addExplicitExtension(language, annotator);
}
try {
List<Annotator> list = LanguageAnnotators.INSTANCE.allForLanguage(language);
assertTrue(list.toString(), list.containsAll(Arrays.asList(annotators)));
runnable.run();
for (MyRecordingAnnotator annotator : annotators) {
assertTrue(annotator +" must have done something but didn't", annotator.didIDoIt());
}
}
finally {
for (int i = annotators.length - 1; i >= 0; i--) {
Annotator annotator = annotators[i];
LanguageAnnotators.INSTANCE.removeExplicitExtension(language, annotator);
for (Map.Entry<com.intellij.lang.Language, MyRecordingAnnotator[]> entry : annotatorsByLanguage.entrySet()) {
com.intellij.lang.Language language = entry.getKey();
MyRecordingAnnotator[] annotators = entry.getValue();
for (Annotator annotator : annotators) {
LanguageAnnotators.INSTANCE.addExplicitExtension(language, annotator);
}
}
List<Annotator> list = LanguageAnnotators.INSTANCE.allForLanguage(language);
for (Annotator annotator : annotators) {
assertFalse(list.toString(), list.contains(annotator));
try {
for (Map.Entry<com.intellij.lang.Language, MyRecordingAnnotator[]> entry : annotatorsByLanguage.entrySet()) {
com.intellij.lang.Language language = entry.getKey();
MyRecordingAnnotator[] annotators = entry.getValue();
List<Annotator> list = LanguageAnnotators.INSTANCE.allForLanguage(language);
assertTrue(list.toString(), list.containsAll(Arrays.asList(annotators)));
}
runnable.run();
for (Map.Entry<com.intellij.lang.Language, MyRecordingAnnotator[]> entry : annotatorsByLanguage.entrySet()) {
MyRecordingAnnotator[] annotators = entry.getValue();
for (MyRecordingAnnotator annotator : annotators) {
assertTrue(annotator + " must have done something but didn't", annotator.didIDoIt());
}
}
}
finally {
for (Map.Entry<com.intellij.lang.Language, MyRecordingAnnotator[]> entry : annotatorsByLanguage.entrySet()) {
com.intellij.lang.Language language = entry.getKey();
MyRecordingAnnotator[] annotators = entry.getValue();
for (int i = annotators.length - 1; i >= 0; i--) {
Annotator annotator = annotators[i];
LanguageAnnotators.INSTANCE.removeExplicitExtension(language, annotator);
}
}
}
for (Map.Entry<com.intellij.lang.Language, MyRecordingAnnotator[]> entry : annotatorsByLanguage.entrySet()) {
com.intellij.lang.Language language = entry.getKey();
MyRecordingAnnotator[] annotators = entry.getValue();
List<Annotator> list = LanguageAnnotators.INSTANCE.allForLanguage(language);
for (Annotator annotator : annotators) {
assertFalse(list.toString(), list.contains(annotator));
}
}
}
@@ -2509,38 +2540,49 @@ public class DaemonRespondToChangesTest extends DaemonAnalyzerTestCase {
public void testAddAnnotationViaBuilderEntailsCreatingCorrespondingRangeHighlighterImmediately() {
if (!ensureEnoughParallelism()) return;
useAnnotatorsIn(JavaFileType.INSTANCE.getLanguage(), new MyRecordingAnnotator[]{new MyNewBuilderAnnotator(), }, this::checkSwearingAnnotationIsVisibleImmediately);
useAnnotatorsIn(JavaFileType.INSTANCE.getLanguage(), new MyNewBuilderAnnotator(), this::checkSwearingAnnotationIsVisibleImmediately);
}
private static final AtomicBoolean annotated = new AtomicBoolean();
private static final AtomicBoolean injectedAnnotated = new AtomicBoolean();
private static final AtomicBoolean inspected = new AtomicBoolean();
public static class MySlowAnnotator extends MyRecordingAnnotator {
@Override
public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) {
if (element instanceof PsiFile) {
assertFalse("For this moment file has not to process injected fragments", injectedAnnotated.get());
assertFalse("For this moment file has not to run inspections", inspected.get());
annotated.set(true);
iDidIt();
}
assertFalse(inspected.get());
}
}
public void test_RunInspectionsAfterCompletionOfGeneralHighlightPass_SecretSettingDoesWork() {
public static class MyInjectedSlowAnnotator extends MyRecordingAnnotator {
@Override
public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) {
assertTrue("File already has to be annotated", annotated.get());
injectedAnnotated.set(true);
iDidIt();
}
}
public void test_SerializeCodeInsightPasses_SecretSettingDoesWork() {
if (!ensureEnoughParallelism()) return;
TextEditorHighlightingPassRegistrarImpl registrar =
(TextEditorHighlightingPassRegistrarImpl)TextEditorHighlightingPassRegistrar.getInstance(myProject);
assertFalse("Somebody (rogue plugin?) has left the dangerous setting on", registrar.isRunInspectionsAfterCompletionOfGeneralHighlightPass());
assertFalse("Somebody (rogue plugin?) has left the dangerous setting on", registrar.isSerializeCodeInsightPasses());
registerInspection(new LocalInspectionTool() {
@Override
public @NotNull String getID() {
return getTestName(false)+"MySlowInspectionTool";
}
@Override
public ProblemDescriptor @Nullable [] checkFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) {
inspected.set(true);
return null;
}
@Override
public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder,
boolean isOnTheFly,
@@ -2548,21 +2590,34 @@ public class DaemonRespondToChangesTest extends DaemonAnalyzerTestCase {
return new PsiElementVisitor() {
@Override
public void visitElement(@NotNull PsiElement element) {
assertTrue(annotated.get());
assertTrue("File has to be already annotated", annotated.get());
inspected.set(true);
}
};
}
});
try {
myDaemonCodeAnalyzer.runLocalInspectionPassAfterCompletionOfGeneralHighlightPass(true);
myDaemonCodeAnalyzer.serializeCodeInsightPasses(true);
Map<com.intellij.lang.Language, MyRecordingAnnotator @NotNull []> annotatorsByLanguage = new HashMap<>();
annotatorsByLanguage.put(JavaLanguage.INSTANCE, new MyRecordingAnnotator[]{new MySlowAnnotator()});
annotatorsByLanguage.put(XMLLanguage.INSTANCE, new MyRecordingAnnotator[]{new MyInjectedSlowAnnotator()});
useAnnotatorsIn(annotatorsByLanguage, () -> {
configureByText(JavaFileType.INSTANCE,
"class X{\n" +
"// language=XML\n" +
"String ql = \"<value>1</value>\";" +
"\n}");
useAnnotatorsIn(JavaFileType.INSTANCE.getLanguage(), new MyRecordingAnnotator[]{new MySlowAnnotator(), }, () -> {
configureByText(JavaFileType.INSTANCE, "class X{}");
doHighlighting();
assertTrue("File already has to be java annotated", annotated.get());
assertTrue("File already has to annotate xml injection", injectedAnnotated.get());
assertTrue("File already has to run inspections", inspected.get());
});
}
finally {
myDaemonCodeAnalyzer.runLocalInspectionPassAfterCompletionOfGeneralHighlightPass(false);
myDaemonCodeAnalyzer.serializeCodeInsightPasses(false);
}
}
@@ -2575,7 +2630,7 @@ public class DaemonRespondToChangesTest extends DaemonAnalyzerTestCase {
public void testTypingMustRescheduleDaemonBackByReparseDelayMillis() {
EmptyAnnotator emptyAnnotator = new EmptyAnnotator();
executeWithReparseDelay(2000, () ->
useAnnotatorsIn(JavaLanguage.INSTANCE, new MyRecordingAnnotator[]{emptyAnnotator}, () -> {
useAnnotatorsIn(JavaLanguage.INSTANCE, emptyAnnotator, () -> {
@Language("JAVA")
String text = "class X {\n}";
configureByText(JavaFileType.INSTANCE, text);
@@ -2606,7 +2661,7 @@ public class DaemonRespondToChangesTest extends DaemonAnalyzerTestCase {
}
public void testDaemonDoesReportTheFirstProducedAnnotation() {
useAnnotatorsIn(JavaFileType.INSTANCE.getLanguage(), new MyRecordingAnnotator[]{new MyInfoAnnotator()}, () -> checkFirstAnnotation());
useAnnotatorsIn(JavaFileType.INSTANCE.getLanguage(), new MyInfoAnnotator(), () -> checkFirstAnnotation());
}
private void checkFirstAnnotation() {
@@ -38,7 +38,7 @@ public final class TextEditorHighlightingPassRegistrarImpl extends TextEditorHig
private final AtomicInteger nextAvailableId = new AtomicInteger();
private boolean checkedForCycles;
private final Project myProject;
private boolean runInspectionsAfterCompletionOfGeneralHighlightPass;
private boolean serializeCodeInsightPasses;
public TextEditorHighlightingPassRegistrarImpl(@NotNull Project project) {
myProject = project;
@@ -76,14 +76,14 @@ public final class TextEditorHighlightingPassRegistrarImpl extends TextEditorHig
}
@ApiStatus.Internal
void runInspectionsAfterCompletionOfGeneralHighlightPass(boolean flag) {
runInspectionsAfterCompletionOfGeneralHighlightPass = flag;
void serializeCodeInsightPasses(boolean flag) {
serializeCodeInsightPasses = flag;
reRegisterFactories();
}
@ApiStatus.Internal
boolean isRunInspectionsAfterCompletionOfGeneralHighlightPass() {
return runInspectionsAfterCompletionOfGeneralHighlightPass;
boolean isSerializeCodeInsightPasses() {
return serializeCodeInsightPasses;
}
private static final class PassConfig {
@@ -982,20 +982,38 @@ public final class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzerEx implement
return result;
}
/**
* @deprecated Use {@link DaemonCodeAnalyzerImpl#serializeCodeInsightPasses(boolean)} instead
*/
@Deprecated
@ApiStatus.Internal
public void runLocalInspectionPassAfterCompletionOfGeneralHighlightPass(boolean flag) {
serializeCodeInsightPasses(flag);
}
/**
* This API is made {@code Internal} intentionally as it could lead to unpredictable highlighting performance behaviour.
*
* @param flag if {@code true}: enables code insight passes serialization:
* Injected fragments {@link InjectedGeneralHighlightingPass} highlighting and Inspections run after
* completion of Syntax analysis {@link GeneralHighlightingPass}.
* if {@code false} (default behaviour) code insight passes are running in parallel
*/
@ApiStatus.Internal
public void serializeCodeInsightPasses(boolean flag) {
ApplicationManager.getApplication().assertIsDispatchThread();
setUpdateByTimerEnabled(false);
try {
cancelUpdateProgress(false, "runLocalInspectionPassAfterCompletionOfGeneralHighlightPass");
cancelUpdateProgress(false, "serializeCodeInsightPasses");
myPassExecutorService.cancelAll(true);
TextEditorHighlightingPassRegistrarImpl registrar =
(TextEditorHighlightingPassRegistrarImpl)TextEditorHighlightingPassRegistrar.getInstance(myProject);
registrar.runInspectionsAfterCompletionOfGeneralHighlightPass(flag);
registrar.serializeCodeInsightPasses(flag);
}
finally {
setUpdateByTimerEnabled(true);
}
}
}
@@ -7,13 +7,18 @@ import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.ProperTextRange;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
public final class InjectedGeneralHighlightingPassFactory implements MainHighlightingPassFactory, TextEditorHighlightingPassFactoryRegistrar {
@Override
public void registerHighlightingPassFactory(@NotNull TextEditorHighlightingPassRegistrar registrar, @NotNull Project project) {
registrar.registerTextEditorHighlightingPass(this, null, new int[]{Pass.UPDATE_ALL}, false, -1);
boolean serialized = Registry.is("editor.injected.highlighting.serialization.allowed") &&
((TextEditorHighlightingPassRegistrarImpl)registrar).isSerializeCodeInsightPasses();
int[] ghl = {Pass.UPDATE_ALL};
registrar.registerTextEditorHighlightingPass(this, serialized ? ghl : null,
serialized ? null : ghl, false, -1);
}
@NotNull
@@ -18,11 +18,11 @@ public final class LocalInspectionsPassFactory implements MainHighlightingPassFa
@Override
public void registerHighlightingPassFactory(@NotNull TextEditorHighlightingPassRegistrar registrar, @NotNull Project project) {
int[] GHP = {Pass.UPDATE_ALL};
boolean runInspectionsAfterCompletionOfGeneralHighlightPass =
((TextEditorHighlightingPassRegistrarImpl)registrar).isRunInspectionsAfterCompletionOfGeneralHighlightPass();
registrar.registerTextEditorHighlightingPass(this, runInspectionsAfterCompletionOfGeneralHighlightPass ? GHP : null,
runInspectionsAfterCompletionOfGeneralHighlightPass ? null : GHP, true, Pass.LOCAL_INSPECTIONS);
int[] ghp = {Pass.UPDATE_ALL};
boolean serializeCodeInsightPasses =
((TextEditorHighlightingPassRegistrarImpl)registrar).isSerializeCodeInsightPasses();
registrar.registerTextEditorHighlightingPass(this, serializeCodeInsightPasses ? ghp : null,
serializeCodeInsightPasses ? null : ghp, true, Pass.LOCAL_INSPECTIONS);
}
@NotNull
@@ -937,6 +937,9 @@ ide.progress.indeterminate.by.default=true
editor.injected.highlighting.enabled=true
editor.injected.highlighting.enabled.description=Disables injected fragments highlighting (requires project reopening)
editor.injected.highlighting.serialization.allowed=true
editor.injected.highlighting.serialization.allowed.description=Allow injected fragments highlighting after syntax highlighting (requires project reopening)
# CPP-8395 Registry setting run.processes.with.pty not saved
# Upper-case FALSE makes an option explicitly turned off distinct from the default value
run.processes.with.pty=FALSE