Merge remote-tracking branch 'origin/master' into develar/is
[idea/community.git] / java / java-tests / testSrc / com / intellij / codeInsight / daemon / impl / DaemonRespondToChangesTest.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.codeInsight.daemon.impl;
17
18 import com.intellij.codeHighlighting.*;
19 import com.intellij.codeInsight.EditorInfo;
20 import com.intellij.codeInsight.completion.CompletionContributor;
21 import com.intellij.codeInsight.daemon.*;
22 import com.intellij.codeInsight.daemon.impl.quickfix.DeleteCatchFix;
23 import com.intellij.codeInsight.daemon.quickFix.LightQuickFixTestCase;
24 import com.intellij.codeInsight.folding.CodeFoldingManager;
25 import com.intellij.codeInsight.generation.actions.CommentByLineCommentAction;
26 import com.intellij.codeInsight.hint.EditorHintListener;
27 import com.intellij.codeInsight.intention.AbstractIntentionAction;
28 import com.intellij.codeInsight.intention.IntentionAction;
29 import com.intellij.codeInsight.intention.IntentionManager;
30 import com.intellij.codeInsight.intention.impl.IntentionHintComponent;
31 import com.intellij.codeInspection.InspectionProfile;
32 import com.intellij.codeInspection.LocalInspectionTool;
33 import com.intellij.codeInspection.ProblemsHolder;
34 import com.intellij.codeInspection.accessStaticViaInstance.AccessStaticViaInstance;
35 import com.intellij.codeInspection.deadCode.UnusedDeclarationInspection;
36 import com.intellij.codeInspection.deadCode.UnusedDeclarationInspectionBase;
37 import com.intellij.codeInspection.ex.InspectionProfileImpl;
38 import com.intellij.codeInspection.ex.InspectionToolRegistrar;
39 import com.intellij.codeInspection.ex.InspectionToolWrapper;
40 import com.intellij.codeInspection.ex.LocalInspectionToolWrapper;
41 import com.intellij.codeInspection.htmlInspections.RequiredAttributesInspectionBase;
42 import com.intellij.codeInspection.varScopeCanBeNarrowed.FieldCanBeLocalInspection;
43 import com.intellij.diagnostic.PerformanceWatcher;
44 import com.intellij.execution.filters.TextConsoleBuilderFactory;
45 import com.intellij.execution.ui.ConsoleView;
46 import com.intellij.execution.ui.ConsoleViewContentType;
47 import com.intellij.ide.DataManager;
48 import com.intellij.ide.GeneralSettings;
49 import com.intellij.ide.SaveAndSyncHandlerImpl;
50 import com.intellij.ide.highlighter.JavaFileType;
51 import com.intellij.javaee.ExternalResourceManagerExImpl;
52 import com.intellij.lang.CompositeLanguage;
53 import com.intellij.lang.ExternalLanguageAnnotators;
54 import com.intellij.lang.LanguageFilter;
55 import com.intellij.lang.StdLanguages;
56 import com.intellij.lang.annotation.AnnotationHolder;
57 import com.intellij.lang.annotation.ExternalAnnotator;
58 import com.intellij.lang.annotation.HighlightSeverity;
59 import com.intellij.lang.java.JavaLanguage;
60 import com.intellij.openapi.Disposable;
61 import com.intellij.openapi.actionSystem.*;
62 import com.intellij.openapi.actionSystem.impl.SimpleDataContext;
63 import com.intellij.openapi.application.AccessToken;
64 import com.intellij.openapi.application.ApplicationManager;
65 import com.intellij.openapi.application.Result;
66 import com.intellij.openapi.application.WriteAction;
67 import com.intellij.openapi.application.ex.ApplicationEx;
68 import com.intellij.openapi.application.ex.ApplicationManagerEx;
69 import com.intellij.openapi.command.CommandProcessor;
70 import com.intellij.openapi.command.WriteCommandAction;
71 import com.intellij.openapi.command.undo.UndoManager;
72 import com.intellij.openapi.components.AbstractProjectComponent;
73 import com.intellij.openapi.components.impl.stores.StorageUtil;
74 import com.intellij.openapi.editor.*;
75 import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
76 import com.intellij.openapi.editor.actionSystem.EditorActionManager;
77 import com.intellij.openapi.editor.actionSystem.TypedAction;
78 import com.intellij.openapi.editor.ex.EditorEx;
79 import com.intellij.openapi.editor.ex.MarkupModelEx;
80 import com.intellij.openapi.editor.ex.RangeHighlighterEx;
81 import com.intellij.openapi.editor.impl.DocumentMarkupModel;
82 import com.intellij.openapi.editor.impl.EditorImpl;
83 import com.intellij.openapi.editor.impl.event.MarkupModelListener;
84 import com.intellij.openapi.editor.markup.GutterIconRenderer;
85 import com.intellij.openapi.editor.markup.HighlighterTargetArea;
86 import com.intellij.openapi.editor.markup.MarkupModel;
87 import com.intellij.openapi.editor.markup.RangeHighlighter;
88 import com.intellij.openapi.fileEditor.FileEditorManager;
89 import com.intellij.openapi.fileEditor.OpenFileDescriptor;
90 import com.intellij.openapi.fileEditor.TextEditor;
91 import com.intellij.openapi.fileEditor.impl.text.PsiAwareTextEditorProvider;
92 import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider;
93 import com.intellij.openapi.fileTypes.PlainTextFileType;
94 import com.intellij.openapi.fileTypes.StdFileTypes;
95 import com.intellij.openapi.module.Module;
96 import com.intellij.openapi.progress.ProcessCanceledException;
97 import com.intellij.openapi.progress.ProgressIndicator;
98 import com.intellij.openapi.progress.impl.CoreProgressManager;
99 import com.intellij.openapi.project.Project;
100 import com.intellij.openapi.project.ProjectManager;
101 import com.intellij.openapi.project.ex.ProjectManagerEx;
102 import com.intellij.openapi.project.impl.ProjectManagerImpl;
103 import com.intellij.openapi.util.Disposer;
104 import com.intellij.openapi.util.ProperTextRange;
105 import com.intellij.openapi.util.Segment;
106 import com.intellij.openapi.util.TextRange;
107 import com.intellij.openapi.util.text.StringUtil;
108 import com.intellij.openapi.vfs.VirtualFile;
109 import com.intellij.profile.codeInspection.InspectionProfileManager;
110 import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
111 import com.intellij.psi.*;
112 import com.intellij.psi.impl.DebugUtil;
113 import com.intellij.psi.search.GlobalSearchScope;
114 import com.intellij.refactoring.inline.InlineRefactoringActionHandler;
115 import com.intellij.refactoring.rename.RenameProcessor;
116 import com.intellij.testFramework.*;
117 import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl;
118 import com.intellij.ui.HintListener;
119 import com.intellij.ui.LightweightHint;
120 import com.intellij.util.*;
121 import com.intellij.util.containers.ContainerUtil;
122 import com.intellij.util.io.storage.HeavyProcessLatch;
123 import com.intellij.util.ui.UIUtil;
124 import com.intellij.xml.util.CheckDtdReferencesInspection;
125 import gnu.trove.THashSet;
126 import org.jetbrains.annotations.Nls;
127 import org.jetbrains.annotations.NonNls;
128 import org.jetbrains.annotations.NotNull;
129 import org.jetbrains.annotations.Nullable;
130
131 import java.awt.*;
132 import java.io.File;
133 import java.io.IOException;
134 import java.lang.reflect.Method;
135 import java.util.*;
136 import java.util.List;
137 import java.util.concurrent.atomic.AtomicBoolean;
138 import java.util.concurrent.atomic.AtomicLong;
139 import java.util.concurrent.atomic.AtomicReference;
140
141 /**
142  * @author cdr
143  */
144 @SuppressWarnings("StringConcatenationInsideStringBufferAppend")
145 @SkipSlowTestLocally
146 public class DaemonRespondToChangesTest extends DaemonAnalyzerTestCase {
147   private static final String BASE_PATH = "/codeInsight/daemonCodeAnalyzer/typing/";
148
149   private DaemonCodeAnalyzerImpl myDaemonCodeAnalyzer;
150
151   @Override
152   protected void setUp() throws Exception {
153     super.setUp();
154     enableInspectionTool(new UnusedDeclarationInspection());
155     myDaemonCodeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
156     UndoManager.getInstance(myProject);
157     myDaemonCodeAnalyzer.setUpdateByTimerEnabled(true);
158     DaemonProgressIndicator.setDebug(true);
159   }
160
161   @Override
162   protected void tearDown() throws Exception {
163     try {
164       Project project = getProject();
165       if (project != null) {
166         doPostponedFormatting(project);
167         ((ProjectManagerImpl)ProjectManagerEx.getInstanceEx()).closeProject(project, false, false, false);
168       }
169     }
170     finally {
171       super.tearDown();
172     }
173   }
174
175   @Override
176   protected boolean doTestLineMarkers() {
177     return true;
178   }
179
180   @Override
181   protected void setUpProject() throws Exception {
182     super.setUpProject();
183     ProjectManagerEx.getInstanceEx().openProject(getProject());
184     super.runStartupActivities();
185     UIUtil.dispatchAllInvocationEvents(); // startup activities
186   }
187
188   @Override
189   protected void runStartupActivities() {
190
191   }
192
193   private static void typeInAlienEditor(Editor alienEditor, char c) {
194     TypedAction action = EditorActionManager.getInstance().getTypedAction();
195     DataContext dataContext = ((EditorEx)alienEditor).getDataContext();
196
197     action.actionPerformed(alienEditor, c, dataContext);
198   }
199
200   
201   public void testHighlightersUpdate() throws Exception {
202     configureByFile(BASE_PATH + "HighlightersUpdate.java");
203     Document document = getDocument(getFile());
204     highlightErrors();
205     List<HighlightInfo> errors = DaemonCodeAnalyzerImpl.getHighlights(document, HighlightSeverity.ERROR, getProject());
206     assertEquals(1, errors.size());
207     TextRange dirty = myDaemonCodeAnalyzer.getFileStatusMap().getFileDirtyScope(document, Pass.UPDATE_ALL);
208     assertNull(dirty);
209
210     type(' ');
211     PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
212     dirty = myDaemonCodeAnalyzer.getFileStatusMap().getFileDirtyScope(document, Pass.UPDATE_ALL);
213     assertNotNull(dirty);
214   }
215
216   
217   public void testNoPsiEventsAltogether() throws Exception {
218     configureByFile(BASE_PATH + "HighlightersUpdate.java");
219     Document document = getDocument(getFile());
220     highlightErrors();
221     type(' ');
222     backspace();
223     PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
224
225     TextRange dirty = myDaemonCodeAnalyzer.getFileStatusMap().getFileDirtyScope(document, Pass.UPDATE_ALL);
226     assertEquals(getFile().getTextRange(), dirty); // have to rehighlight whole file in case no PSI events have come
227   }
228
229   public void testRenameClass() throws Exception {
230     configureByFile(BASE_PATH + "AClass.java");
231     Document document = getDocument(getFile());
232     Collection<HighlightInfo> infos = highlightErrors();
233     assertEquals(0, infos.size());
234     final PsiClass psiClass = ((PsiJavaFile)getFile()).getClasses()[0];
235     ApplicationManager.getApplication().runWriteAction(() -> {
236       new RenameProcessor(myProject, psiClass, "Class2", false, false).run();
237
238       TextRange dirty = myDaemonCodeAnalyzer.getFileStatusMap().getFileDirtyScope(document, Pass.UPDATE_ALL);
239       assertEquals(getFile().getTextRange(), dirty);
240     });
241
242     highlightErrors();
243     assertTrue(myDaemonCodeAnalyzer.isErrorAnalyzingFinished(getFile()));
244   }
245
246   
247   public void testTypingSpace() throws Exception {
248     configureByFile(BASE_PATH + "AClass.java");
249     Document document = getDocument(getFile());
250     Collection<HighlightInfo> infos = highlightErrors();
251     assertEquals(0, infos.size());
252
253     type("  ");
254     PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
255     PsiElement elementAtCaret = myFile.findElementAt(myEditor.getCaretModel().getOffset());
256     assertTrue(elementAtCaret instanceof PsiWhiteSpace);
257
258     TextRange dirty = myDaemonCodeAnalyzer.getFileStatusMap().getFileDirtyScope(document, Pass.UPDATE_ALL);
259     assertEquals(elementAtCaret.getTextRange(), dirty);
260     highlightErrors();
261     assertTrue(myDaemonCodeAnalyzer.isErrorAnalyzingFinished(getFile()));
262   }
263
264   
265   public void testTypingSpaceInsideError() throws Exception {
266     configureByFile(BASE_PATH + "Error.java");
267     Collection<HighlightInfo> infos = highlightErrors();
268     assertEquals(1, infos.size());
269
270     for (int i = 0; i < 100; i++) {
271       type(" ");
272       List<HighlightInfo> errors = highlightErrors();
273       assertEquals(1, errors.size());
274     }
275   }
276
277   
278   public void testBackSpaceInsideError() throws Exception {
279     configureByFile(BASE_PATH + "BackError.java");
280     Collection<HighlightInfo> infos = highlightErrors();
281     assertEquals(1, infos.size());
282
283     backspace();
284     List<HighlightInfo> errors = highlightErrors();
285     assertEquals(1, errors.size());
286   }
287
288   @Override
289   protected LocalInspectionTool[] configureLocalInspectionTools() {
290     if (isPerformanceTest() && !getTestName(false).equals("TypingCurliesClearsEndOfFileErrorsInPhp_ItIsPerformanceTestBecauseItRunsTooLong")) {
291       // all possible inspections
292       List<InspectionToolWrapper> all = InspectionToolRegistrar.getInstance().createTools();
293       List<LocalInspectionTool> locals = new ArrayList<>();
294       all.stream().filter(tool -> tool instanceof LocalInspectionToolWrapper).forEach(tool -> {
295         LocalInspectionTool e = ((LocalInspectionToolWrapper)tool).getTool();
296         locals.add(e);
297       });
298       return locals.toArray(new LocalInspectionTool[locals.size()]);
299     }
300     return new LocalInspectionTool[]{
301       new FieldCanBeLocalInspection(),
302       new RequiredAttributesInspectionBase(),
303       new CheckDtdReferencesInspection(),
304       new AccessStaticViaInstance(),
305     };
306   }
307
308   
309   public void testUnusedFieldUpdate() throws Exception {
310     configureByFile(BASE_PATH + "UnusedField.java");
311     Document document = getDocument(getFile());
312     List<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING);
313     assertEquals(1, infos.size());
314     assertEquals("Private field 'ffff' is never used", infos.get(0).getDescription());
315
316     type("  foo(ffff++);");
317     highlightErrors();
318
319     List<HighlightInfo> errors = DaemonCodeAnalyzerImpl.getHighlights(document, HighlightSeverity.WARNING, getProject());
320     assertEquals(0, errors.size());
321   }
322
323   public void testUnusedMethodUpdate() throws Exception {
324     configureByText(JavaFileType.INSTANCE, "class X {\n" +
325                                            "    static void ffff() {}\n" +
326                                            "    public static void main(String[] args){\n" +
327                                            "        for (int i=0; i<1000;i++) {\n" +
328                                            "            System.out.println(i);\n" +
329                                            "            <caret>ffff();\n" +
330                                            "        }\n" +
331                                            "    }\n}");
332     enableInspectionTool(new UnusedDeclarationInspection(true));
333     List<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING);
334     assertEmpty(infos);
335
336     commentLine();
337     infos = doHighlighting(HighlightSeverity.WARNING);
338
339     assertEquals(1, infos.size());
340     assertEquals("Method 'ffff()' is never used", infos.get(0).getDescription());
341   }
342
343
344   public void testAssignedButUnreadFieldUpdate() throws Exception {
345     configureByFile(BASE_PATH + "AssignedButUnreadField.java");
346     List<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING);
347     assertEquals(1, infos.size());
348     assertEquals("Private field 'text' is assigned but never accessed", infos.get(0).getDescription());
349
350     ctrlW();
351     WriteCommandAction.runWriteCommandAction(getProject(), () -> {
352       EditorModificationUtil.deleteSelectedText(getEditor());
353       type("  text");
354     });
355
356     List<HighlightInfo> errors = doHighlighting(HighlightSeverity.WARNING);
357     assertEmpty(getFile().getText(), errors);
358   }
359
360   public void testDaemonIgnoresNonPhysicalEditor() throws Exception {
361     configureByFile(BASE_PATH + "AClass.java");
362     highlightErrors();
363
364     EditorFactory editorFactory = EditorFactory.getInstance();
365     final Document consoleDoc = editorFactory.createDocument("my console blah");
366     final Editor consoleEditor = editorFactory.createEditor(consoleDoc);
367
368     try {
369       checkDaemonReaction(false, () -> caretRight(consoleEditor));
370       checkDaemonReaction(true, () -> typeInAlienEditor(consoleEditor, 'x'));
371       checkDaemonReaction(true, () -> LightPlatformCodeInsightTestCase.backspace(consoleEditor, getProject()));
372
373       //real editor
374       checkDaemonReaction(true, this::caretRight);
375     }
376     finally {
377       editorFactory.releaseEditor(consoleEditor);
378     }
379   }
380
381   
382   public void testDaemonIgnoresConsoleActivities() throws Exception {
383     configureByFile(BASE_PATH + "AClass.java");
384     doHighlighting(HighlightSeverity.WARNING);
385
386     final ConsoleView consoleView = TextConsoleBuilderFactory.getInstance().createBuilder(getProject()).getConsole();
387
388     consoleView.getComponent(); //create editor
389     consoleView.print("haha", ConsoleViewContentType.NORMAL_OUTPUT);
390     UIUtil.dispatchAllInvocationEvents();
391
392     try {
393       checkDaemonReaction(false, () -> {
394         consoleView.clear();
395         try {
396           Thread.sleep(300); // *&^ing alarm
397         }
398         catch (InterruptedException e) {
399           LOG.error(e);
400         }
401         UIUtil.dispatchAllInvocationEvents(); //flush
402       });
403       checkDaemonReaction(false, () -> {
404         consoleView.print("sss", ConsoleViewContentType.NORMAL_OUTPUT);
405         try {
406           Thread.sleep(300); // *&^ing alarm
407         }
408         catch (InterruptedException e) {
409           LOG.error(e);
410         }
411         UIUtil.dispatchAllInvocationEvents(); //flush
412       });
413       checkDaemonReaction(false, () -> {
414         consoleView.setOutputPaused(true);
415         try {
416           Thread.sleep(300); // *&^ing alarm
417         }
418         catch (InterruptedException e) {
419           LOG.error(e);
420         }
421         UIUtil.dispatchAllInvocationEvents(); //flush
422       });
423     }
424     finally {
425       Disposer.dispose(consoleView);
426     }
427   }
428
429   private void checkDaemonReaction(boolean mustCancelItself, @NotNull final Runnable action) {
430     PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
431     highlightErrors();
432     myDaemonCodeAnalyzer.waitForTermination();
433     TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(getEditor());
434
435     final AtomicBoolean run = new AtomicBoolean();
436     Disposable disposable = Disposer.newDisposable();
437     final AtomicReference<RuntimeException> stopDaemonReason = new AtomicReference<>();
438     StorageUtil.DEBUG_LOG = "";
439     getProject().getMessageBus().connect(disposable).subscribe(DaemonCodeAnalyzer.DAEMON_EVENT_TOPIC,
440             new DaemonCodeAnalyzer.DaemonListenerAdapter() {
441               @Override
442               public void daemonCancelEventOccurred(@NotNull String reason) {
443                 stopDaemonReason.compareAndSet(null, new RuntimeException("Some bastard's restarted daemon: " + reason + "\nStorage write log: "+
444                                                                           StorageUtil.DEBUG_LOG));
445               }
446             });
447     try {
448       while (true) {
449         try {
450           myDaemonCodeAnalyzer.runPasses(getFile(), getDocument(getFile()), textEditor, new int[0], true, () -> {
451             if (!run.getAndSet(true)) {
452               action.run();
453             }
454           });
455           break;
456         }
457         catch (ProcessCanceledException ignored) { }
458       }
459
460       if (mustCancelItself) {
461         assertNotNull(stopDaemonReason.get());
462       }
463       else {
464         if (stopDaemonReason.get() != null) throw stopDaemonReason.get();
465       }
466     }
467     finally {
468       StorageUtil.DEBUG_LOG = null;
469       Disposer.dispose(disposable);
470     }
471   }
472
473   
474   public void testWholeFileInspection() throws Exception {
475     configureByFile(BASE_PATH + "FieldCanBeLocal.java");
476     List<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING);
477     assertEquals(1, infos.size());
478     assertEquals("Field can be converted to a local variable", infos.get(0).getDescription());
479
480     ctrlW();
481     WriteCommandAction.runWriteCommandAction(getProject(), () -> {
482       EditorModificationUtil.deleteSelectedText(getEditor());
483       type("xxxx");
484     });
485
486     infos = doHighlighting(HighlightSeverity.WARNING);
487     assertEmpty(infos);
488
489     ctrlW();
490     WriteCommandAction.runWriteCommandAction(getProject(), () -> {
491       EditorModificationUtil.deleteSelectedText(getEditor());
492       type("0");
493     });
494
495     infos = doHighlighting(HighlightSeverity.WARNING);
496     assertEquals(1, infos.size());
497     assertEquals("Field can be converted to a local variable", infos.get(0).getDescription());
498   }
499
500   private static class MyWholeInspection extends LocalInspectionTool {
501     private final List<PsiElement> visited = Collections.synchronizedList(new ArrayList<>());
502
503     @Nls
504     @NotNull
505     @Override
506     public String getGroupDisplayName() {
507       return "fegna";
508     }
509
510     @Nls
511     @NotNull
512     @Override
513     public String getDisplayName() {
514       return getGroupDisplayName();
515     }
516
517     @NotNull
518     @Override
519     public String getShortName() {
520       return getGroupDisplayName();
521     }
522
523     @NotNull
524     @Override
525     public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
526       return new PsiElementVisitor() {
527         @Override
528         public void visitFile(PsiFile file) {
529           TimeoutUtil.sleep(1000); // make it run longer that LIP
530           super.visitFile(file);
531         }
532
533         @Override
534         public void visitElement(PsiElement element) {
535           visited.add(element);
536           super.visitElement(element);
537         }
538       };
539     }
540
541     @Override
542     public boolean runForWholeFile() {
543       return true;
544     }
545   }
546
547   public void testWholeFileInspectionRestartedOnAllElements() throws Exception {
548     MyWholeInspection tool = new MyWholeInspection();
549     enableInspectionTool(tool);
550     disposeOnTearDown(() -> disableInspectionTool(tool.getShortName()));
551
552     configureByText(JavaFileType.INSTANCE, "class X { void f() { <caret> } }");
553     List<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING);
554     assertEmpty(infos);
555     int visitedCount = new HashSet<>(tool.visited).size();
556     tool.visited.clear();
557
558     type(" ");  // white space modification
559
560     infos = doHighlighting(HighlightSeverity.WARNING);
561     assertEmpty(infos);
562
563     int countAfter = new HashSet<>(tool.visited).size();
564     assertTrue("visitedCount = "+visitedCount+"; countAfter="+countAfter, countAfter >= visitedCount);
565   }
566
567   public void testWholeFileInspectionRestartedEvenIfThereWasAModificationInsideCodeBlockInOtherFile() throws Exception {
568     MyWholeInspection tool = new MyWholeInspection();
569
570     enableInspectionTool(tool);
571     disposeOnTearDown(() -> disableInspectionTool(tool.getShortName()));
572
573     PsiFile file = configureByText(JavaFileType.INSTANCE, "class X { void f() { <caret> } }");
574     PsiFile otherFile = createFile(myModule, file.getContainingDirectory().getVirtualFile(), "otherFile.txt", "xxx");
575     List<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING);
576     assertEmpty(infos);
577     int visitedCount = tool.visited.size();
578     assertTrue(tool.visited.toString(), visitedCount > 0);
579     tool.visited.clear();
580
581     Document otherDocument = PsiDocumentManager.getInstance(getProject()).getDocument(otherFile);
582     WriteCommandAction.runWriteCommandAction(getProject(), () -> otherDocument.setText("zzz"));
583
584     infos = doHighlighting(HighlightSeverity.WARNING);
585     assertEmpty(infos);
586
587     int countAfter = tool.visited.size();
588     assertTrue(tool.visited.toString(), countAfter > 0);
589   }
590
591   public void testOverriddenMethodMarkers() throws Exception {
592     configureByFile(BASE_PATH + getTestName(false) + ".java");
593     highlightErrors();
594
595     Document document = getEditor().getDocument();
596     List<LineMarkerInfo> markers = DaemonCodeAnalyzerImpl.getLineMarkers(document, getProject());
597     assertEquals(3, markers.size());
598
599     type("//xxxx");
600
601     highlightErrors();
602     markers = DaemonCodeAnalyzerImpl.getLineMarkers(document, getProject());
603     assertEquals(3, markers.size());
604   }
605
606   
607   public void testOverriddenMethodMarkersDoNotClearedByCHangingWhitespaceNearby() throws Exception {
608     configureByFile(BASE_PATH + "OverriddenMethodMarkers.java");
609     highlightErrors();
610
611     Document document = getEditor().getDocument();
612     List<LineMarkerInfo> markers = DaemonCodeAnalyzerImpl.getLineMarkers(document, getProject());
613     assertEquals(3, markers.size());
614
615     PsiElement element = ((PsiJavaFile)myFile).getClasses()[0].findMethodsByName("f", false)[0].getReturnTypeElement().getNextSibling();
616     assertEquals("   ", element.getText());
617     getEditor().getCaretModel().moveToOffset(element.getTextOffset() + 1);
618     type(" ");
619
620     highlightErrors();
621     markers = DaemonCodeAnalyzerImpl.getLineMarkers(document, getProject());
622     assertEquals(3, markers.size());
623   }
624
625   private static void commentLine() {
626     WriteCommandAction.runWriteCommandAction(null, () -> {
627       CommentByLineCommentAction action = new CommentByLineCommentAction();
628       action.actionPerformed(AnActionEvent.createFromAnAction(action, null, "", DataManager.getInstance().getDataContext()));
629     });
630   }
631
632   
633   public void testChangeXmlIncludeLeadsToRehighlight() throws Exception {
634     LanguageFilter[] extensions = ((CompositeLanguage)StdLanguages.XML).getLanguageExtensions();
635     for (LanguageFilter extension : extensions) {
636       ((CompositeLanguage)StdLanguages.XML).unregisterLanguageExtension(extension);
637     }
638
639     final String location = getTestName(false) + ".xsd";
640     final String url = "http://myschema/";
641     ExternalResourceManagerExImpl.registerResourceTemporarily(url, location, getTestRootDisposable());
642
643     configureByFiles(null, BASE_PATH + getTestName(false) + ".xml", BASE_PATH + getTestName(false) + ".xsd");
644
645     Collection<HighlightInfo> errors = highlightErrors();
646     assertEquals(0, errors.size());
647
648     Editor[] allEditors = EditorFactory.getInstance().getAllEditors();
649     Editor schemaEditor = null;
650     for (Editor editor : allEditors) {
651       Document document = editor.getDocument();
652       PsiFile file = PsiDocumentManager.getInstance(getProject()).getPsiFile(document);
653       if (file == null) continue;
654       if (location.equals(file.getName())) {
655         schemaEditor = editor;
656         break;
657       }
658     }
659     delete(schemaEditor);
660
661     errors = highlightErrors();
662     assertFalse(errors.isEmpty());
663
664     for (LanguageFilter extension : extensions) {
665       ((CompositeLanguage)StdLanguages.XML).registerLanguageExtension(extension);
666     }
667   }
668
669   
670   public void testRehighlightInnerBlockAfterInline() throws Exception {
671     configureByFile(BASE_PATH + getTestName(false) + ".java");
672
673     Collection<HighlightInfo> errors = highlightErrors();
674     HighlightInfo error = assertOneElement(errors);
675     assertEquals("Variable 'e' is already defined in the scope", error.getDescription());
676     PsiElement element = getFile().findElementAt(getEditor().getCaretModel().getOffset()).getParent();
677
678     DataContext dataContext = SimpleDataContext.getSimpleContext(CommonDataKeys.PSI_ELEMENT.getName(), element, ((EditorEx)getEditor()).getDataContext());
679     new InlineRefactoringActionHandler().invoke(getProject(), getEditor(), getFile(), dataContext);
680
681     Collection<HighlightInfo> afterTyping = highlightErrors();
682     assertEmpty(afterTyping);
683   }
684
685   
686   public void testRangeMarkersDoNotGetAddedOrRemovedWhenUserIsJustTypingInsideHighlightedRegionAndEspeciallyInsideInjectedFragmentsWhichAreColoredGreenAndUsersComplainEndlesslyThatEditorFlickersThere()
687     throws Throwable {
688     configureByText(JavaFileType.INSTANCE, "class S { int f() {\n" +
689                                            "    return <caret>hashCode();\n" +
690                                            "}}");
691
692     Collection<HighlightInfo> infos = doHighlighting(HighlightInfoType.SYMBOL_TYPE_SEVERITY);
693     assertEquals(3, infos.size());
694
695     final int[] count = {0};
696     MarkupModelEx modelEx = (MarkupModelEx)DocumentMarkupModel.forDocument(getDocument(getFile()), getProject(), true);
697     modelEx.addMarkupModelListener(getTestRootDisposable(), new MarkupModelListener.Adapter() {
698       @Override
699       public void afterAdded(@NotNull RangeHighlighterEx highlighter) {
700         count[0]++;
701       }
702
703       @Override
704       public void beforeRemoved(@NotNull RangeHighlighterEx highlighter) {
705         count[0]++;
706       }
707     });
708
709     type(' ');
710     highlightErrors();
711
712     assertEquals(0, count[0]);
713   }
714
715   public void testLineMarkersReuse() throws Throwable {
716     configureByFile(BASE_PATH + "LineMarkerChange.java");
717
718     List<HighlightInfo> errors = highlightErrors();
719     assertEmpty(errors);
720
721     List<LineMarkerInfo> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
722     assertSize(5, lineMarkers);
723
724     type('X');
725
726     final Collection<String> changed = new ArrayList<>();
727     MarkupModelEx modelEx = (MarkupModelEx)DocumentMarkupModel.forDocument(getDocument(getFile()), getProject(), true);
728     modelEx.addMarkupModelListener(getTestRootDisposable(), new MarkupModelListener() {
729       @Override
730       public void afterAdded(@NotNull RangeHighlighterEx highlighter) {
731         changed(highlighter, ExceptionUtil.getThrowableText(new Throwable("after added")));
732       }
733
734       @Override
735       public void beforeRemoved(@NotNull RangeHighlighterEx highlighter) {
736         changed(highlighter, ExceptionUtil.getThrowableText(new Throwable("before removed")));
737       }
738
739       @Override
740       public void attributesChanged(@NotNull RangeHighlighterEx highlighter, boolean renderersChanged, boolean fontStyleChanged) {
741         changed(highlighter, ExceptionUtil.getThrowableText(new Throwable("changed")));
742       }
743
744       private void changed(@NotNull RangeHighlighterEx highlighter, String reason) {
745         if (highlighter.getTargetArea() != HighlighterTargetArea.LINES_IN_RANGE) return; // not line marker
746         List<LineMarkerInfo> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
747         if (ContainerUtil.find(lineMarkers, lm -> lm.highlighter == highlighter) == null) return; // not line marker
748
749         changed.add(highlighter+": \n"+reason);
750       }
751     });
752
753     PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
754     List<HighlightInfo> infosAfter = CodeInsightTestFixtureImpl.instantiateAndRun(myFile, myEditor, new int[]{/*Pass.UPDATE_ALL, Pass.LOCAL_INSPECTIONS*/}, false);
755     assertNotEmpty(filter(infosAfter, HighlightSeverity.ERROR));
756
757     assertEmpty(changed);
758     List<LineMarkerInfo> lineMarkersAfter = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
759     assertEquals(lineMarkersAfter.size(), lineMarkers.size());
760   }
761
762   public void testLineMarkersDoNotBlinkOnBackSpaceRightBeforeMethodIdentifier() throws Throwable {
763     configureByText(JavaFileType.INSTANCE, "package x; \n" +
764                                            "class  <caret>ToRun{\n" +
765                                            "  public static void main(String[] args) {\n"+
766                                            "  }\n"+
767                                            "}");
768
769     List<HighlightInfo> errors = highlightErrors();
770     assertEmpty(errors);
771
772     List<LineMarkerInfo> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
773     assertSize(2, lineMarkers);
774
775     backspace();
776
777     final Collection<String> changed = new ArrayList<>();
778     MarkupModelEx modelEx = (MarkupModelEx)DocumentMarkupModel.forDocument(getDocument(getFile()), getProject(), true);
779     modelEx.addMarkupModelListener(getTestRootDisposable(), new MarkupModelListener() {
780       @Override
781       public void afterAdded(@NotNull RangeHighlighterEx highlighter) {
782         changed(highlighter, ExceptionUtil.getThrowableText(new Throwable("after added")));
783       }
784
785       @Override
786       public void beforeRemoved(@NotNull RangeHighlighterEx highlighter) {
787         changed(highlighter, ExceptionUtil.getThrowableText(new Throwable("before removed")));
788       }
789
790       @Override
791       public void attributesChanged(@NotNull RangeHighlighterEx highlighter, boolean renderersChanged, boolean fontStyleChanged) {
792         changed(highlighter, ExceptionUtil.getThrowableText(new Throwable("changed")));
793       }
794
795       private void changed(@NotNull RangeHighlighterEx highlighter, String reason) {
796         if (highlighter.getTargetArea() != HighlighterTargetArea.LINES_IN_RANGE) return; // not line marker
797         List<LineMarkerInfo> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
798         if (ContainerUtil.find(lineMarkers, lm -> lm.highlighter == highlighter) == null) return; // not line marker
799
800         changed.add(highlighter+": \n"+reason);
801       }
802     });
803
804     assertEmpty(highlightErrors());
805
806     assertSize(2, DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject()));
807
808     assertEmpty(changed);
809   }
810
811   public void testTypeParametersMustNotBlinkWhenTypingInsideClass() throws Throwable {
812     configureByText(JavaFileType.INSTANCE, "package x; \n" +
813                                            "abstract class ToRun<TTTTTTTTTTTTTTT> implements Comparable<TTTTTTTTTTTTTTT> {\n" +
814                                            "  private ToRun<TTTTTTTTTTTTTTT> delegate;\n"+
815                                            "  <caret>\n"+
816                                            "  \n"+
817                                            "}");
818
819     List<HighlightInfo> errors = highlightErrors();
820     assertEmpty(errors);
821
822     MarkupModelEx modelEx = (MarkupModelEx)DocumentMarkupModel.forDocument(getDocument(getFile()), getProject(), true);
823     modelEx.addMarkupModelListener(getTestRootDisposable(), new MarkupModelListener.Adapter() {
824       @Override
825       public void beforeRemoved(@NotNull RangeHighlighterEx highlighter) {
826         if (TextRange.create(highlighter).substring(highlighter.getDocument().getText()).equals("TTTTTTTTTTTTTTT")) {
827           throw new RuntimeException("Must not remove type parameter highlighter");
828         }
829       }
830     });
831
832     assertEmpty(highlightErrors());
833
834     type("//xxx");
835     assertEmpty(highlightErrors());
836     backspace();
837     assertEmpty(highlightErrors());
838     backspace();
839     assertEmpty(highlightErrors());
840     backspace();
841     assertEmpty(highlightErrors());
842     backspace();
843     backspace();
844     assertEmpty(highlightErrors());
845   }
846
847   public void testOverrideMethodsHighlightingPersistWhenTypeInsideMethodBody() throws Throwable {
848     configureByText(JavaFileType.INSTANCE, "package x; \n" +
849                                            "class ClassA {\n" +
850                                            "    static <T> void sayHello(Class<? extends T> msg) {}\n" +
851                                            "}\n" +
852
853                                            "class ClassB extends ClassA {\n" +
854                                            "    static <T extends String> void sayHello(Class<? extends T> msg) {<caret>\n" +
855                                            "    }\n" +
856                                            "}\n");
857
858     assertSize(1, highlightErrors());
859     type("//my comment inside method body, so class modifier won't be visited");
860     assertSize(1, highlightErrors());
861   }
862
863   public void testLineMarkersClearWhenTypingAtTheEndOfPsiComment() throws Throwable {
864     configureByText(JavaFileType.INSTANCE, "class S {\n//ddd<caret>\n}");
865     StringBuffer log = new StringBuffer();
866     final LineMarkerProvider provider = new LineMarkerProvider() {
867       @Nullable
868       @Override
869       public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement element) {
870         log.append("getLineMarkerInfo(" + element + ")\n");
871         if (element instanceof PsiComment) {
872           LineMarkerInfo<PsiComment> info = new LineMarkerInfo<>((PsiComment)element, element.getTextRange(), null, Pass.UPDATE_ALL, null, null, GutterIconRenderer.Alignment.LEFT);
873           log.append(info + "\n");
874           return info;
875         }
876         return null;
877       }
878
879       @Override
880       public void collectSlowLineMarkers(@NotNull List<PsiElement> elements, @NotNull Collection<LineMarkerInfo> result) {
881
882       }
883     };
884     LineMarkerProviders.INSTANCE.addExplicitExtension(JavaLanguage.INSTANCE, provider);
885     Disposer.register(myTestRootDisposable, () -> LineMarkerProviders.INSTANCE.removeExplicitExtension(JavaLanguage.INSTANCE, provider));
886     myDaemonCodeAnalyzer.restart();
887     try {
888       List<HighlightInfo> infos = doHighlighting();
889       log.append("File text: '" + getFile().getText() + "'\n");
890       log.append("infos: " + infos + "\n");
891       assertEmpty(filter(infos,HighlightSeverity.ERROR));
892
893       List<LineMarkerInfo> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
894       assertOneElement(lineMarkers);
895
896       type(' ');
897       infos = doHighlighting();
898       log.append("File text: '" + getFile().getText() + "'\n");
899       log.append("infos: " + infos + "\n");
900       assertEmpty(filter(infos,HighlightSeverity.ERROR));
901
902       lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
903       assertOneElement(lineMarkers);
904
905       backspace();
906       infos = doHighlighting();
907       log.append("File text: '" + getFile().getText() + "'\n");
908       log.append("infos: " + infos + "\n");
909       assertEmpty(filter(infos,HighlightSeverity.ERROR));
910
911       lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
912       assertOneElement(lineMarkers);
913     }
914     catch (AssertionError e) {
915       System.err.println("Log:\n"+log+"\n---");
916       throw e;
917     }
918   }
919
920   public void testWhenTypingOverWrongReferenceItsColorChangesToBlackAndOnlyAfterHighlightingFinishedItReturnsToRed() throws Throwable {
921     configureByText(StdFileTypes.JAVA, "class S {  int f() {\n" +
922                                        "    return asfsdfsdfsd<caret>;\n" +
923                                        "}}");
924
925     Collection<HighlightInfo> errors = highlightErrors();
926     assertOneElement(errors);
927     assertSame(HighlightInfoType.WRONG_REF, errors.iterator().next().type);
928
929     Document document = getDocument(getFile());
930
931     type("xxx");
932
933     List<HighlightInfo> infos = DaemonCodeAnalyzerImpl.getHighlights(document, HighlightInfoType.SYMBOL_TYPE_SEVERITY, getProject());
934     for (HighlightInfo info : infos) {
935       assertNotSame(HighlightInfoType.WRONG_REF, info.type);
936     }
937
938     errors = highlightErrors();
939     assertOneElement(errors);
940     assertSame(HighlightInfoType.WRONG_REF, errors.iterator().next().type);
941   }
942
943   
944   public void testQuickFixRemainsAvailableAfterAnotherFixHasBeenAppliedInTheSameCodeBlockBefore() throws Exception {
945     configureByFile(BASE_PATH + "QuickFixes.java");
946
947     DaemonCodeAnalyzerSettings settings = DaemonCodeAnalyzerSettings.getInstance();
948     boolean old = settings.NEXT_ERROR_ACTION_GOES_TO_ERRORS_FIRST;
949     settings.NEXT_ERROR_ACTION_GOES_TO_ERRORS_FIRST = true;
950
951     try {
952       Collection<HighlightInfo> errors = highlightErrors();
953       assertEquals(3, errors.size());
954       new GotoNextErrorHandler(true).invoke(getProject(), getEditor(), getFile());
955
956       List<IntentionAction> fixes = LightQuickFixTestCase.getAvailableActions(getEditor(), getFile());
957       IntentionAction fix = assertContainsOneOf(fixes, DeleteCatchFix.class);
958       assertEquals("Delete catch for 'java.io.IOException'", fix.getText());
959
960       final IntentionAction finalFix = fix;
961       WriteCommandAction.runWriteCommandAction(getProject(), () -> finalFix.invoke(getProject(), getEditor(), getFile()));
962
963       errors = highlightErrors();
964       assertEquals(2, errors.size());
965
966       new GotoNextErrorHandler(true).invoke(getProject(), getEditor(), getFile());
967       fixes = LightQuickFixTestCase.getAvailableActions(getEditor(), getFile());
968       fix = assertContainsOneOf(fixes, DeleteCatchFix.class);
969       assertEquals("Delete catch for 'java.io.IOException'", fix.getText());
970
971       final IntentionAction finalFix1 = fix;
972       WriteCommandAction.runWriteCommandAction(getProject(), () -> finalFix1.invoke(getProject(), getEditor(), getFile()));
973
974       errors = highlightErrors();
975       assertOneElement(errors);
976
977       new GotoNextErrorHandler(true).invoke(getProject(), getEditor(), getFile());
978       fixes = LightQuickFixTestCase.getAvailableActions(getEditor(), getFile());
979       fix = assertContainsOneOf(fixes, DeleteCatchFix.class);
980       assertEquals("Delete catch for 'java.io.IOException'", fix.getText());
981
982       final IntentionAction finalFix2 = fix;
983       WriteCommandAction.runWriteCommandAction(getProject(), () -> finalFix2.invoke(getProject(), getEditor(), getFile()));
984
985       errors = highlightErrors();
986       assertEmpty(errors);
987     }
988     finally {
989       settings.NEXT_ERROR_ACTION_GOES_TO_ERRORS_FIRST = old;
990     }
991   }
992
993   private static <T> T assertContainsOneOf(@NotNull Collection<T> collection, @NotNull Class<?> aClass) {
994     T result = null;
995     for (T t : collection) {
996       if (aClass.isInstance(t)) {
997         if (result != null) {
998           fail("multiple " + aClass.getName() + " objects present in collection " + collection);
999         }
1000         else {
1001           result = t;
1002         }
1003       }
1004     }
1005     assertNotNull(aClass.getName() + " object not found in collection " + collection, result);
1006     return result;
1007   }
1008
1009   
1010   public void testRangeHighlightersDoNotGetStuckForever() throws Throwable {
1011     configureByText(StdFileTypes.JAVA, "class S { void ffffff() {fff<caret>fff();}}");
1012
1013     List<HighlightInfo> infos = highlightErrors();
1014     assertEmpty(infos);
1015     MarkupModel markup = DocumentMarkupModel.forDocument(myEditor.getDocument(), getProject(), true);
1016     final TextRange[] highlightersBefore = getHighlightersTextRange(markup);
1017
1018     type("%%%%");
1019     highlightErrors();
1020     backspace();
1021     backspace();
1022     backspace();
1023     backspace();
1024     infos = highlightErrors();
1025     assertEmpty(infos);
1026
1027     final TextRange[] highlightersAfter = getHighlightersTextRange(markup);
1028
1029     assertEquals(highlightersBefore.length, highlightersAfter.length);
1030     for (int i = 0; i < highlightersBefore.length; i++) {
1031       TextRange before = highlightersBefore[i];
1032       TextRange after = highlightersAfter[i];
1033       assertEquals(before.getStartOffset(), after.getStartOffset());
1034       assertEquals(before.getEndOffset(), after.getEndOffset());
1035     }
1036   }
1037
1038   @NotNull
1039   private static TextRange[] getHighlightersTextRange(@NotNull MarkupModel markup) {
1040     final RangeHighlighter[] highlighters = markup.getAllHighlighters();
1041
1042     final TextRange[] result = new TextRange[highlighters.length];
1043     for (int i = 0; i < highlighters.length; i++) {
1044       result[i] = ProperTextRange.create(highlighters[i]);
1045     }
1046     return orderByHashCode(result); // markup.getAllHighlighters returns unordered array
1047   }
1048
1049   @NotNull
1050   private static <T extends Segment> T[] orderByHashCode(@NotNull T[] highlighters) {
1051     Arrays.sort(highlighters, (o1, o2) -> o2.hashCode() - o1.hashCode());
1052     return highlighters;
1053   }
1054
1055   public void testFileStatusMapDirtyCachingWorks() throws Throwable {
1056     myDaemonCodeAnalyzer.setUpdateByTimerEnabled(false); // to prevent auto-start highlighting
1057     UIUtil.dispatchAllInvocationEvents();
1058     configureByText(StdFileTypes.JAVA, "class <caret>S { int ffffff =  0;}");
1059     UIUtil.dispatchAllInvocationEvents();
1060
1061     final int[] creation = {0};
1062     class Fac extends AbstractProjectComponent implements TextEditorHighlightingPassFactory {
1063       private Fac(Project project) {
1064         super(project);
1065       }
1066
1067       @Override
1068       public TextEditorHighlightingPass createHighlightingPass(@NotNull PsiFile file, @NotNull Editor editor) {
1069         TextRange textRange = FileStatusMap.getDirtyTextRange(editor, Pass.UPDATE_ALL);
1070         if (textRange == null) return null;
1071         return new MyPass(myProject);
1072       }
1073
1074       class MyPass extends TextEditorHighlightingPass {
1075         private MyPass(final Project project) {
1076           super(project, getEditor().getDocument(), false);
1077           creation[0]++;
1078         }
1079
1080         @Override
1081         public void doCollectInformation(@NotNull ProgressIndicator progress) {
1082         }
1083
1084         @Override
1085         public void doApplyInformationToEditor() {
1086         }
1087       }
1088     }
1089     TextEditorHighlightingPassRegistrar registrar = TextEditorHighlightingPassRegistrar.getInstance(getProject());
1090     registrar.registerTextEditorHighlightingPass(new Fac(getProject()), null, null, false, -1);
1091     highlightErrors();
1092     assertEquals(1, creation[0]);
1093
1094     //cached
1095     highlightErrors();
1096     assertEquals(1, creation[0]);
1097     highlightErrors();
1098     assertEquals(1, creation[0]);
1099
1100     type(' ');
1101     highlightErrors();
1102     assertEquals(2, creation[0]);
1103     highlightErrors();
1104     assertEquals(2, creation[0]);
1105     highlightErrors();
1106     assertEquals(2, creation[0]);
1107   }
1108
1109   
1110   public void testDefensivelyDirtyFlagDoesNotClearPrematurely() throws Throwable {
1111     class Fac extends AbstractProjectComponent implements TextEditorHighlightingPassFactory {
1112       private Fac(Project project) {
1113         super(project);
1114       }
1115
1116       @Override
1117       public TextEditorHighlightingPass createHighlightingPass(@NotNull PsiFile file, @NotNull Editor editor) {
1118         return null;
1119       }
1120     }
1121     TextEditorHighlightingPassRegistrar registrar = TextEditorHighlightingPassRegistrar.getInstance(getProject());
1122     registrar.registerTextEditorHighlightingPass(new Fac(getProject()), null, null, false, -1);
1123
1124     configureByText(StdFileTypes.JAVA, "@Deprecated<caret> class S { } ");
1125
1126     List<HighlightInfo> infos = doHighlighting(HighlightInfoType.SYMBOL_TYPE_SEVERITY);
1127     assertEquals(2, infos.size());
1128
1129     assertEquals("@Deprecated", infos.get(0).getText());
1130     assertEquals("S", infos.get(1).getText());
1131
1132     backspace();
1133     type('d');
1134
1135     List<HighlightInfo> after = doHighlighting(HighlightInfoType.SYMBOL_TYPE_SEVERITY);
1136
1137     assertEquals("@Deprecated", after.get(0).getText());
1138     assertEquals("S", after.get(1).getText());
1139
1140     backspace();
1141     type('d');
1142
1143     getEditor().getCaretModel().moveToOffset(getEditor().getDocument().getTextLength());
1144     type(" ");
1145
1146     after = doHighlighting(HighlightInfoType.SYMBOL_TYPE_SEVERITY);
1147     assertEquals(2, after.size());
1148
1149     assertEquals("@Deprecated", after.get(0).getText());
1150     assertEquals("S", after.get(1).getText());
1151   }
1152
1153   
1154   public void testModificationInsideCodeblockDoesnotAffectErrorMarkersOutside() throws Exception {
1155     configureByFile(BASE_PATH + "ErrorMark.java");
1156     List<HighlightInfo> errs = highlightErrors();
1157     assertEquals(1, errs.size());
1158     assertEquals("'}' expected", errs.get(0).getDescription());
1159
1160     type("//comment");
1161     errs = highlightErrors();
1162     assertEquals(1, errs.size());
1163     assertEquals("'}' expected", errs.get(0).getDescription());
1164   }
1165
1166   public void testErrorMarkerAtTheEndOfTheFile() throws Exception {
1167     CommandProcessor.getInstance().executeCommand(getProject(), () -> {
1168       try {
1169         configureByFile(BASE_PATH + "ErrorMarkAtEnd.java");
1170       }
1171       catch (Exception e) {
1172         LOG.error(e);
1173       }
1174     }, "cc", this);
1175     List<HighlightInfo> errs = highlightErrors();
1176     assertEmpty(errs);
1177     CommandProcessor.getInstance().executeCommand(getProject(), () -> {
1178       Document document = getEditor().getDocument();
1179       int offset = getEditor().getCaretModel().getOffset();
1180       while (offset < document.getTextLength()) {
1181         int i = StringUtil.indexOf(document.getText(), '}', offset, document.getTextLength());
1182         if (i == -1) break;
1183         getEditor().getCaretModel().moveToOffset(i);
1184         delete(getEditor());
1185       }
1186     }, "my", this);
1187
1188     errs = highlightErrors();
1189     assertEquals(2, errs.size());
1190     assertEquals("'}' expected", errs.get(0).getDescription());
1191
1192     undo();
1193     errs = highlightErrors();
1194     assertEmpty(errs);
1195   }
1196
1197
1198   // disabled for now
1199   public void _testSOEInEndlessAppendChainPerformance() throws Throwable {
1200     StringBuilder text = new StringBuilder("class S { String ffffff =  new StringBuilder()\n");
1201     for (int i=0; i<2000; i++) {
1202       text.append(".append(").append(i).append(")\n");
1203     }
1204     text.append(".toString();<caret>}");
1205     configureByText(StdFileTypes.JAVA, text.toString());
1206
1207     PlatformTestUtil.startPerformanceTest("too many tree visitors", 30000, () -> {
1208       List<HighlightInfo> infos = highlightErrors();
1209       assertEmpty(infos);
1210       type("kjhgas");
1211       List<HighlightInfo> errors = highlightErrors();
1212       assertFalse(errors.isEmpty());
1213       backspace();
1214       backspace();
1215       backspace();
1216       backspace();
1217       backspace();
1218       backspace();
1219       infos = highlightErrors();
1220       assertEmpty(infos);
1221     }).useLegacyScaling().assertTiming();
1222   }
1223
1224   
1225   public void testBulbAppearsAfterType() throws Throwable {
1226     String text = "class S { ArrayList<caret>XXX x;}";
1227     configureByText(StdFileTypes.JAVA, text);
1228
1229     ((EditorImpl)myEditor).getScrollPane().getViewport().setSize(1000, 1000);
1230     DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true);
1231
1232     final Set<LightweightHint> shown = ContainerUtil.newIdentityTroveSet();
1233     getProject().getMessageBus().connect().subscribe(EditorHintListener.TOPIC, (project, hint, flags) -> {
1234       shown.add(hint);
1235       hint.addHintListener(event -> shown.remove(hint));
1236     });
1237
1238     DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
1239     highlightErrors();
1240
1241     IntentionHintComponent hintComponent = codeAnalyzer.getLastIntentionHint();
1242     assertNotNull(hintComponent);
1243     assertFalse(hintComponent.isDisposed());
1244     assertNotNull(hintComponent.getComponentHint());
1245     assertTrue(shown.contains(hintComponent.getComponentHint()));
1246
1247     type("x");
1248     highlightErrors();
1249     hintComponent = codeAnalyzer.getLastIntentionHint();
1250     assertNotNull(hintComponent);
1251     assertFalse(hintComponent.isDisposed());
1252     assertNotNull(hintComponent.getComponentHint());
1253     assertTrue(shown.contains(hintComponent.getComponentHint()));
1254   }
1255
1256   @Override
1257   protected void configureByExistingFile(@NotNull VirtualFile virtualFile) {
1258     super.configureByExistingFile(virtualFile);
1259     EditorTracker editorTracker = getProject().getComponent(EditorTracker.class);
1260     editorTracker.setActiveEditors(Collections.singletonList(getEditor()));
1261   }
1262
1263   @Override
1264   protected VirtualFile configureByFiles(@Nullable File rawProjectRoot, @NotNull VirtualFile... vFiles) throws IOException {
1265     VirtualFile file = super.configureByFiles(rawProjectRoot, vFiles);
1266     EditorTracker editorTracker = getProject().getComponent(EditorTracker.class);
1267     editorTracker.setActiveEditors(Collections.singletonList(getEditor()));
1268     return file;
1269   }
1270
1271   public void testDaemonIgnoresFrameDeactivation() throws Throwable {
1272     DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true); // return default value to avoid unnecessary save
1273     InspectionProfileManager.getInstance().setRootProfile(InspectionProfileImpl.getDefaultProfile().getName()); // reset to default profile from the custom one to avoid unnecessary save
1274
1275     String text = "class S { ArrayList<caret>XXX x;}";
1276     configureByText(StdFileTypes.JAVA, text);
1277     highlightErrors();
1278
1279     GeneralSettings settings = GeneralSettings.getInstance();
1280     ApplicationEx application = ApplicationManagerEx.getApplicationEx();
1281     boolean frameSave = settings.isSaveOnFrameDeactivation();
1282     boolean appSave = application.isDoNotSave();
1283
1284     settings.setSaveOnFrameDeactivation(true);
1285     application.doNotSave(false);
1286     try {
1287       SaveAndSyncHandlerImpl.doSaveDocumentsAndProjectsAndApp();
1288
1289       checkDaemonReaction(false, SaveAndSyncHandlerImpl::doSaveDocumentsAndProjectsAndApp);
1290     }
1291     finally {
1292       application.doNotSave(appSave);
1293       settings.setSaveOnFrameDeactivation(frameSave);
1294     }
1295   }
1296
1297
1298   public void testApplyLocalQuickFix() throws Throwable {
1299     configureByText(StdFileTypes.JAVA, "class X { static int sss; public int f() { return this.<caret>sss; }}");
1300
1301     ((EditorImpl)myEditor).getScrollPane().getViewport().setSize(1000, 1000);
1302     DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true);
1303
1304     List<HighlightInfo> warns = doHighlighting(HighlightSeverity.WARNING);
1305     assertOneElement(warns);
1306     List<HighlightInfo.IntentionActionDescriptor> actions = ShowIntentionsPass.getAvailableActions(getEditor(), getFile(), -1);
1307     final HighlightInfo.IntentionActionDescriptor descriptor = assertOneElement(actions);
1308     WriteCommandAction.runWriteCommandAction(getProject(), () -> descriptor.getAction().invoke(getProject(), getEditor(), getFile()));
1309
1310     highlightErrors();
1311     actions = ShowIntentionsPass.getAvailableActions(getEditor(), getFile(), -1);
1312     assertEmpty(actions);
1313   }
1314
1315   
1316   public void testApplyErrorInTheMiddle() throws Throwable {
1317     String text = "class <caret>X { ";
1318     for (int i = 0; i < 100; i++) {
1319       text += "\n    {\n" +
1320               "//    String x = \"<zzzzzzzzzz/>\";\n" +
1321               "    }";
1322     }
1323     text += "\n}";
1324     configureByText(StdFileTypes.JAVA, text);
1325
1326     ((EditorImpl)myEditor).getScrollPane().getViewport().setSize(1000, 1000);
1327     DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true);
1328
1329     List<HighlightInfo> warns = highlightErrors();
1330     assertEmpty(warns);
1331
1332     type("//");
1333     List<HighlightInfo> errors = highlightErrors();
1334     assertEquals(2, errors.size());
1335
1336     backspace();
1337     backspace();
1338
1339     errors = highlightErrors();
1340     assertEmpty(errors);
1341   }
1342
1343   
1344   public void testErrorInTheEndOutsideVisibleArea() throws Throwable {
1345     String text = "<xml> \n" + StringUtil.repeatSymbol('\n', 1000) + "</xml>\nxxxxx<caret>";
1346     configureByText(StdFileTypes.XML, text);
1347
1348     ProperTextRange visibleRange = makeEditorWindowVisible(new Point(0, 1000));
1349     assertTrue(visibleRange.getStartOffset() > 0);
1350
1351     List<HighlightInfo> warns = highlightErrors();
1352     HighlightInfo info = assertOneElement(warns);
1353     assertEquals("Top level element is not completed", info.getDescription());
1354
1355     type("xxx");
1356     List<HighlightInfo> errors = highlightErrors();
1357     info = assertOneElement(errors);
1358     assertEquals("Top level element is not completed", info.getDescription());
1359   }
1360
1361   private ProperTextRange makeEditorWindowVisible(Point viewPosition) {
1362     ((EditorImpl)myEditor).getScrollPane().getViewport().setSize(1000, 1000);
1363     DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true);
1364
1365     myEditor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
1366     ((EditorImpl)myEditor).getScrollPane().getViewport().setViewPosition(viewPosition);
1367     ((EditorImpl)myEditor).getScrollPane().getViewport().setExtentSize(new Dimension(100, ((EditorImpl)myEditor).getPreferredHeight() - 
1368                                                                                           viewPosition.y));
1369     return VisibleHighlightingPassFactory.calculateVisibleRange(getEditor());
1370   }
1371
1372
1373   public void testEnterInCodeBlock() throws Throwable {
1374     String text = "class LQF {\n" +
1375                   "    int wwwwwwwwwwww;\n" +
1376                   "    public void main() {<caret>\n" +
1377                   "        wwwwwwwwwwww = 1;\n" +
1378                   "    }\n" +
1379                   "}";
1380     configureByText(StdFileTypes.JAVA, text);
1381
1382     ((EditorImpl)myEditor).getScrollPane().getViewport().setSize(1000, 1000);
1383
1384     List<HighlightInfo> infos = doHighlighting(HighlightInfoType.SYMBOL_TYPE_SEVERITY);
1385     assertEquals(4, infos.size());
1386
1387     type('\n');
1388     infos = doHighlighting(HighlightInfoType.SYMBOL_TYPE_SEVERITY);
1389     assertEquals(4, infos.size());
1390
1391     deleteLine();
1392
1393     infos = doHighlighting(HighlightInfoType.SYMBOL_TYPE_SEVERITY);
1394     assertEquals(4, infos.size());
1395   }
1396
1397   
1398   public void testTypingNearEmptyErrorElement() throws Throwable {
1399     String text = "class LQF {\n" +
1400                   "    public void main() {\n" +
1401                   "        int wwwwwwwwwwww = 1<caret>\n" +
1402                   "    }\n" +
1403                   "}";
1404     configureByText(StdFileTypes.JAVA, text);
1405
1406     ((EditorImpl)myEditor).getScrollPane().getViewport().setSize(1000, 1000);
1407
1408     List<HighlightInfo> infos = highlightErrors();
1409     assertEquals(1, infos.size());
1410
1411     type(';');
1412     infos = highlightErrors();
1413     assertEmpty(infos);
1414   }
1415
1416   
1417   public void testLIPGetAllParentsAfterCodeBlockModification() throws Throwable {
1418     String text = "class LQF {\n" +
1419                   "    int f;\n" +
1420                   "    public void me() {\n" +
1421                   "        <caret>\n" +
1422                   "    }\n" +
1423                   "}";
1424     configureByText(StdFileTypes.JAVA, text);
1425
1426     final List<PsiElement> visitedElements = Collections.synchronizedList(new ArrayList<>());
1427     class MyVisitor extends PsiElementVisitor {
1428       @Override
1429       public void visitElement(PsiElement element) {
1430         visitedElements.add(element);
1431       }
1432     }
1433     final MyVisitor visitor = new MyVisitor();
1434     final LocalInspectionTool tool = new LocalInspectionTool() {
1435       @Nls
1436       @NotNull
1437       @Override
1438       public String getGroupDisplayName() {
1439         return "fegna";
1440       }
1441
1442       @Nls
1443       @NotNull
1444       @Override
1445       public String getDisplayName() {
1446         return getGroupDisplayName();
1447       }
1448
1449       @NotNull
1450       @Override
1451       public String getShortName() {
1452         return getGroupDisplayName();
1453       }
1454
1455       @NotNull
1456       @Override
1457       public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
1458         return visitor;
1459       }
1460     };
1461     disposeOnTearDown(() -> disableInspectionTool(tool.getShortName()));
1462     enableInspectionTool(tool);
1463
1464     List<HighlightInfo> infos = highlightErrors();
1465     assertEmpty(infos);
1466
1467     List<PsiElement> allPsi = CollectHighlightsUtil.getElementsInRange(myFile, 0, myFile.getTextLength());
1468     assertEquals(new HashSet<>(allPsi), new HashSet<>(visitedElements));
1469
1470     // inside code block modification
1471     visitedElements.clear();
1472     type("//");
1473
1474     infos = highlightErrors();
1475     assertEmpty(infos);
1476
1477
1478     PsiMethod method = ((PsiJavaFile)myFile).getClasses()[0].getMethods()[0];
1479     List<PsiElement> methodAndParents =
1480       CollectHighlightsUtil.getElementsInRange(myFile, method.getTextRange().getStartOffset(), method.getTextRange().getEndOffset(), true);
1481     assertEquals(new HashSet<>(methodAndParents), new HashSet<>(visitedElements));
1482   }
1483
1484   
1485   public void testCancelsItSelfOnTypingInAlienProject() throws Throwable {
1486     String body = StringUtil.repeat("\"String field = null;\"\n", 1000);
1487     configureByText(StdFileTypes.JAVA, "class X{ void f() {" + body + "<caret>\n} }");
1488
1489     File temp = createTempDirectory();
1490     final Project alienProject = createProject(temp + "/alien.ipr", DebugUtil.currentStackTrace());
1491     boolean succ2 = ProjectManagerEx.getInstanceEx().openProject(alienProject);
1492     assertTrue(succ2);
1493     DaemonProgressIndicator.setDebug(true);
1494     final DaemonProgressIndicator[] indicator = new DaemonProgressIndicator[1];
1495
1496     try {
1497       Module alienModule = doCreateRealModuleIn("x", alienProject, getModuleType());
1498       final VirtualFile alienRoot = PsiTestUtil.createTestProjectStructure(alienProject, alienModule, myFilesToDelete);
1499       OpenFileDescriptor alienDescriptor = new WriteAction<OpenFileDescriptor>() {
1500         @Override
1501         protected void run(@NotNull Result<OpenFileDescriptor> result) throws Throwable {
1502           VirtualFile alienFile = alienRoot.createChildData(this, "X.java");
1503           setFileText(alienFile, "class Alien { }");
1504           OpenFileDescriptor alienDescriptor = new OpenFileDescriptor(alienProject, alienFile);
1505           result.setResult(alienDescriptor);
1506         }
1507       }.execute().throwException().getResultObject();
1508
1509       FileEditorManager fe = FileEditorManager.getInstance(alienProject);
1510       final Editor alienEditor = fe.openTextEditor(alienDescriptor, false);
1511       ((EditorImpl)alienEditor).setCaretActive();
1512       PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
1513       PsiDocumentManager.getInstance(alienProject).commitAllDocuments();
1514
1515       // start daemon in main project. should check for its cancel when typing in alien
1516       TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(getEditor());
1517       DaemonCodeAnalyzerImpl di = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
1518       final boolean[] checked = {false};
1519       di.runPasses(getFile(), getEditor().getDocument(), textEditor, ArrayUtil.EMPTY_INT_ARRAY, true, () -> {
1520         if (checked[0]) return;
1521         checked[0] = true;
1522         indicator[0] = myDaemonCodeAnalyzer.getUpdateProgress();
1523         typeInAlienEditor(alienEditor, 'x');
1524       });
1525     }
1526     catch (ProcessCanceledException ignored) {
1527       //DaemonProgressIndicator.setDebug(true);
1528       //System.out.println("indicator = " + indicator[0]);
1529       return;
1530     }
1531     finally {
1532       ProjectManagerEx.getInstanceEx().closeAndDispose(alienProject);
1533     }
1534     fail("must throw PCE");
1535   }
1536
1537   public void testPasteInAnonymousCodeBlock() throws Throwable {
1538     configureByText(StdFileTypes.JAVA, "class X{ void f() {" +
1539                                        "     int x=0;\n" +
1540                                        "    Runnable r = new Runnable() { public void run() {\n" +
1541                                        " <caret>\n" +
1542                                        "    }};\n" +
1543                                        "    <selection>int y = x;</selection>\n " +
1544                                        "\n} }");
1545     assertEmpty(highlightErrors());
1546     copy();
1547     assertEquals("int y = x;", getEditor().getSelectionModel().getSelectedText());
1548     getEditor().getSelectionModel().removeSelection();
1549     paste();
1550     List<HighlightInfo> errors = highlightErrors();
1551     assertEquals(1, errors.size());
1552   }
1553
1554   private void paste() {
1555     EditorActionManager actionManager = EditorActionManager.getInstance();
1556     final EditorActionHandler actionHandler = actionManager.getActionHandler(IdeActions.ACTION_EDITOR_PASTE);
1557     WriteCommandAction.runWriteCommandAction(null, () -> actionHandler.execute(getEditor(), null, DataManager.getInstance().getDataContext()));
1558   }
1559
1560   private void copy() {
1561     EditorActionManager actionManager = EditorActionManager.getInstance();
1562     final EditorActionHandler actionHandler = actionManager.getActionHandler(IdeActions.ACTION_EDITOR_COPY);
1563     WriteCommandAction.runWriteCommandAction(null, () -> actionHandler.execute(getEditor(), null, DataManager.getInstance().getDataContext()));
1564   }
1565
1566   public void testReactivityPerformance() throws Throwable {
1567     @NonNls String filePath = "/psi/resolve/Thinlet.java";
1568     configureByFile(filePath);
1569     type(' ');
1570     CompletionContributor.forLanguage(getFile().getLanguage());
1571     long s = System.currentTimeMillis();
1572     highlightErrors();
1573     long e = System.currentTimeMillis();
1574     System.out.println("Hi elapsed: "+(e-s));
1575
1576     final DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
1577     int N = Math.max(5, Timings.adjustAccordingToMySpeed(80, true));
1578     System.out.println("N = " + N);
1579     final long[] interruptTimes = new long[N];
1580     for (int i = 0; i < N; i++) {
1581       codeAnalyzer.restart();
1582       final int finalI = i;
1583       final long start = System.currentTimeMillis();
1584       final AtomicLong typingStart = new AtomicLong();
1585       Thread watcher = new Thread("reactivity watcher") {
1586         @Override
1587         public void run() {
1588           while (true) {
1589             final long start1 = typingStart.get();
1590             if (start1 == -1) break;
1591             if (start1 == 0) {
1592               try {
1593                 Thread.sleep(5);
1594               }
1595               catch (InterruptedException e1) {
1596                 throw new RuntimeException(e1);
1597               }
1598               continue;
1599             }
1600             long elapsed = System.currentTimeMillis() - start1;
1601             if (elapsed > 500) {
1602               // too long, see WTF
1603               String message = "Too long interrupt: " + elapsed +
1604                                "; Progress: " + codeAnalyzer.getUpdateProgress() +
1605                                "\n----------------------------";
1606               dumpThreadsToConsole();
1607               throw new RuntimeException(message);
1608             }
1609           }
1610         }
1611       };
1612       try {
1613         PsiFile file = getFile();
1614         Editor editor = getEditor();
1615         Project project = file.getProject();
1616         CodeInsightTestFixtureImpl.ensureIndexesUpToDate(project);
1617         TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(editor);
1618         PsiDocumentManager.getInstance(myProject).commitAllDocuments();
1619         watcher.start();
1620         Runnable interrupt = () -> {
1621           long now = System.currentTimeMillis();
1622           if (now - start < 100) {
1623             // wait to engage all highlighting threads
1624             return;
1625           }
1626           typingStart.set(System.currentTimeMillis());
1627           type(' ');
1628           long end = System.currentTimeMillis();
1629           long interruptTime = end - now;
1630           interruptTimes[finalI] = interruptTime;
1631           assertNull(codeAnalyzer.getUpdateProgress());
1632           System.out.println(interruptTime);
1633           throw new ProcessCanceledException();
1634         };
1635         long hiStart = System.currentTimeMillis();
1636         codeAnalyzer.runPasses(file, editor.getDocument(), textEditor, ArrayUtil.EMPTY_INT_ARRAY, false, interrupt);
1637         long hiEnd = System.currentTimeMillis();
1638         DaemonProgressIndicator progress = codeAnalyzer.getUpdateProgress();
1639         String message = "Should have been interrupted: " + progress + "; Elapsed: " + (hiEnd - hiStart) + "ms";
1640         dumpThreadsToConsole();
1641         throw new RuntimeException(message);
1642       }
1643       catch (ProcessCanceledException ignored) {
1644       }
1645       finally {
1646         typingStart.set(-1); // cancel watcher
1647         watcher.join();
1648       }
1649     }
1650
1651     long ave = ArrayUtil.averageAmongMedians(interruptTimes, 3);
1652     System.out.println("Average among the N/3 median times: " + ave + "ms");
1653     assertTrue(ave < 300);
1654   }
1655
1656   private static void dumpThreadsToConsole() {
1657     PerformanceWatcher.dumpThreadsToConsole("");
1658     System.err.println("----all threads---");
1659     for (Thread thread : Thread.getAllStackTraces().keySet()) {
1660       boolean canceled = CoreProgressManager.isCanceledThread(thread);
1661       if (canceled) {
1662         System.err.println("Thread " + thread + " indicator is canceled");
1663       }
1664     }
1665     System.err.println("----///////---");
1666   }
1667
1668   public void testTypingLatencyPerformance() throws Throwable {
1669     @NonNls String filePath = "/psi/resolve/ThinletBig.java";
1670
1671     configureByFile(filePath);
1672
1673     type(' ');
1674     CompletionContributor.forLanguage(getFile().getLanguage());
1675     long s = System.currentTimeMillis();
1676     highlightErrors();
1677     long e = System.currentTimeMillis();
1678     //System.out.println("Hi elapsed: "+(e-s));
1679
1680     final DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
1681     int N = Math.max(5, Timings.adjustAccordingToMySpeed(80, true));
1682     //System.out.println("N = " + N);
1683     final long[] interruptTimes = new long[N];
1684     for (int i = 0; i < N; i++) {
1685       codeAnalyzer.restart();
1686       final int finalI = i;
1687       final long start = System.currentTimeMillis();
1688       Runnable interrupt = () -> {
1689         long now = System.currentTimeMillis();
1690         if (now - start < 100) {
1691           // wait to engage all highlighting threads
1692           return;
1693         }
1694         type(' ');
1695         long end = System.currentTimeMillis();
1696         long interruptTime = end - now;
1697         interruptTimes[finalI] = interruptTime;
1698         assertNull(codeAnalyzer.getUpdateProgress());
1699         //System.out.println(interruptTime);
1700         throw new ProcessCanceledException();
1701       };
1702       try {
1703         PsiFile file = getFile();
1704         Editor editor = getEditor();
1705         Project project = file.getProject();
1706         CodeInsightTestFixtureImpl.ensureIndexesUpToDate(project);
1707         TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(editor);
1708         PsiDocumentManager.getInstance(myProject).commitAllDocuments();
1709         codeAnalyzer.runPasses(file, editor.getDocument(), textEditor, ArrayUtil.EMPTY_INT_ARRAY, false, interrupt);
1710
1711         throw new RuntimeException("should have been interrupted");
1712       }
1713       catch (ProcessCanceledException ignored) {
1714       }
1715       backspace();
1716       //highlightErrors();
1717     }
1718
1719     long mean = ArrayUtil.averageAmongMedians(interruptTimes, 3);
1720     long avg = Arrays.stream(interruptTimes).sum() / interruptTimes.length;
1721     long max = Arrays.stream(interruptTimes).max().getAsLong();
1722     long min = Arrays.stream(interruptTimes).min().getAsLong();
1723     System.out.println("Average among the N/3 median times: " + mean + "ms; max: "+max+"; min:"+min+"; avg: "+avg);
1724     assertTrue(mean < 10);
1725   }
1726
1727   private static void startCPUProfiling() {
1728     try {
1729       Class<?> aClass = Class.forName("com.intellij.util.ProfilingUtil");
1730       Method method = ReflectionUtil.getDeclaredMethod(aClass, "startCPUProfiling");
1731       method.invoke(null);
1732     }
1733     catch (Exception e) {
1734       throw new RuntimeException(e);
1735     }
1736   }
1737   private static void stopCPUProfiling() {
1738     try {
1739       Class<?> aClass = Class.forName("com.intellij.util.ProfilingUtil");
1740       Method method = ReflectionUtil.getDeclaredMethod(aClass, "stopCPUProfiling");
1741       method.invoke(null);
1742     }
1743     catch (Exception e) {
1744       throw new RuntimeException(e);
1745     }
1746   }
1747
1748   private static String captureCPUSnapshot() {
1749     try {
1750       Class<?> aClass = Class.forName("com.intellij.util.ProfilingUtil");
1751       Method method = ReflectionUtil.getDeclaredMethod(aClass, "captureCPUSnapshot");
1752       return (String)method.invoke(null);
1753     }
1754     catch (Exception e) {
1755       throw new RuntimeException(e);
1756     }
1757   }
1758
1759   public void testPostHighlightingPassRunsOnEveryPsiModification() throws Exception {
1760     PsiFile x = createFile("X.java", "public class X { public static void ffffffffffffff(){} }");
1761     PsiFile use = createFile("Use.java", "public class Use { { <caret>X.ffffffffffffff(); } }");
1762     configureByExistingFile(use.getVirtualFile());
1763
1764     InspectionProfile profile = InspectionProjectProfileManager.getInstance(myProject).getCurrentProfile();
1765     HighlightDisplayKey myDeadCodeKey = HighlightDisplayKey.find(UnusedDeclarationInspectionBase.SHORT_NAME);
1766     if (myDeadCodeKey == null) {
1767       myDeadCodeKey = HighlightDisplayKey.register(UnusedDeclarationInspectionBase.SHORT_NAME, UnusedDeclarationInspectionBase.DISPLAY_NAME);
1768     }
1769     UnusedDeclarationInspectionBase myDeadCodeInspection = new UnusedDeclarationInspectionBase(true);
1770     enableInspectionTool(myDeadCodeInspection);
1771     assert profile.isToolEnabled(myDeadCodeKey, myFile);
1772
1773     Editor xEditor = createEditor(x.getVirtualFile());
1774     List<HighlightInfo> xInfos = filter(CodeInsightTestFixtureImpl.instantiateAndRun(x, xEditor, new int[0], false),
1775                                         HighlightSeverity.WARNING);
1776     HighlightInfo info = ContainerUtil.find(xInfos, xInfo -> xInfo.getDescription().equals("Method 'ffffffffffffff()' is never used"));
1777     assertNull(xInfos.toString(), info);
1778
1779     Editor useEditor = myEditor;
1780     List<HighlightInfo> useInfos = filter(CodeInsightTestFixtureImpl.instantiateAndRun(use, useEditor, new int[0], false), HighlightSeverity.ERROR);
1781     assertEmpty(useInfos);
1782
1783     type('/');
1784     type('/');
1785
1786     PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
1787     xInfos = filter(CodeInsightTestFixtureImpl.instantiateAndRun(x, xEditor, new int[0], false), HighlightSeverity.WARNING);
1788     info = ContainerUtil.find(xInfos, xInfo -> xInfo.getDescription().equals("Method 'ffffffffffffff()' is never used"));
1789     assertNotNull(xInfos.toString(), info);
1790   }
1791
1792
1793   public void testErrorDisappearsRightAfterTypingInsideVisibleAreaWhileDaemonContinuesToChugAlong() throws Throwable {
1794     String text = "class X{\nint xxx;\n{\nint i = <selection>null</selection><caret>;\n" + StringUtil.repeat("{ this.hashCode(); }\n\n\n", 10000) + "}}";
1795     configureByText(StdFileTypes.JAVA, text);
1796
1797     ((EditorImpl)myEditor).getScrollPane().getViewport().setSize(100, 100);
1798     DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true);
1799
1800     myEditor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
1801     ((EditorImpl)myEditor).getScrollPane().getViewport().setViewPosition(new Point(0, 0));
1802     ((EditorImpl)myEditor).getScrollPane().getViewport().setExtentSize(new Dimension(100, 100000));
1803     ProperTextRange visibleRange = VisibleHighlightingPassFactory.calculateVisibleRange(getEditor());
1804     assertTrue(visibleRange.getLength() > 0);
1805     final Document document = myEditor.getDocument();
1806     assertTrue(visibleRange.getLength() < document.getTextLength());
1807
1808     List<HighlightInfo> err1 = highlightErrors();
1809     HighlightInfo info = assertOneElement(err1);
1810     final String errorDescription = "Incompatible types. Found: 'null', required: 'int'";
1811     assertEquals(errorDescription, info.getDescription());
1812
1813     MarkupModelEx model = (MarkupModelEx)DocumentMarkupModel.forDocument(document, myProject, false);
1814     final boolean[] errorRemoved = {false};
1815
1816     model.addMarkupModelListener(getTestRootDisposable(), new MarkupModelListener.Adapter() {
1817       @Override
1818       public void beforeRemoved(@NotNull RangeHighlighterEx highlighter) {
1819         Object tt = highlighter.getErrorStripeTooltip();
1820         if (!(tt instanceof HighlightInfo)) return;
1821         String description = ((HighlightInfo)tt).getDescription();
1822         if (errorDescription.equals(description)) {
1823           errorRemoved[0] = true;
1824
1825           List<TextEditorHighlightingPass> passes = myDaemonCodeAnalyzer.getPassesToShowProgressFor(document);
1826           GeneralHighlightingPass ghp = null;
1827           for (TextEditorHighlightingPass pass : passes) {
1828             if (pass instanceof GeneralHighlightingPass && pass.getId() == Pass.UPDATE_ALL) {
1829               assert ghp == null : ghp;
1830               ghp = (GeneralHighlightingPass)pass;
1831             }
1832           }
1833           assertNotNull(ghp);
1834           boolean finished = ghp.isFinished();
1835           assertFalse(finished);
1836         }
1837       }
1838     });
1839     type("1");
1840
1841     List<HighlightInfo> errors = highlightErrors();
1842     assertEmpty(errors);
1843     assertTrue(errorRemoved[0]);
1844   }
1845
1846   public void testDaemonWorksForDefaultProjectSinceItIsNeededInSettingsDialogForSomeReason() {
1847     assertNotNull(DaemonCodeAnalyzer.getInstance(ProjectManager.getInstance().getDefaultProject()));
1848   }
1849
1850   public void testChangeEventsAreNotAlwaysGeneric() throws Exception {
1851     String body = "class X {\n" +
1852                   "<caret>    @org.PPP\n" +
1853                   "    void dtoArrayDouble() {\n" +
1854                   "    }\n" +
1855                   "}";
1856     configureByText(JavaFileType.INSTANCE, body);
1857     makeEditorWindowVisible(new Point());
1858
1859     List<HighlightInfo> errors = highlightErrors();
1860     assertFalse(errors.isEmpty());
1861
1862     type("//");
1863     errors = highlightErrors();
1864     assertEmpty(errors);
1865
1866     backspace();
1867     backspace();
1868     errors = highlightErrors();
1869     assertFalse(errors.isEmpty());
1870   }
1871
1872   public void testInterruptOnTyping() throws Throwable {
1873     @NonNls String filePath = "/psi/resolve/Thinlet.java";
1874     configureByFile(filePath);
1875     highlightErrors();
1876
1877     final DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
1878     codeAnalyzer.restart();
1879     Runnable interrupt = () -> type(' ');
1880     try {
1881       PsiDocumentManager.getInstance(myProject).commitAllDocuments();
1882
1883       PsiFile file = getFile();
1884       Editor editor = getEditor();
1885       Project project = file.getProject();
1886       CodeInsightTestFixtureImpl.ensureIndexesUpToDate(project);
1887       TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(editor);
1888       codeAnalyzer.runPasses(file, editor.getDocument(), textEditor, ArrayUtil.EMPTY_INT_ARRAY, true, interrupt);
1889     }
1890     catch (ProcessCanceledException ignored) {
1891       return;
1892     }
1893     fail("PCE must have been thrown");
1894   }
1895
1896   public void testAllPassesFinishAfterInterruptOnTyping_Performance() throws Throwable {
1897     @NonNls String filePath = "/psi/resolve/Thinlet.java";
1898     configureByFile(filePath);
1899     highlightErrors();
1900
1901     final DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
1902     Runnable interrupt = () -> type(' ');
1903     type(' ');
1904     for (int i=0; i<100; i++) {
1905       backspace();
1906       codeAnalyzer.restart();
1907       try {
1908         PsiDocumentManager.getInstance(myProject).commitAllDocuments();
1909
1910         PsiFile file = getFile();
1911         Editor editor = getEditor();
1912         Project project = file.getProject();
1913         CodeInsightTestFixtureImpl.ensureIndexesUpToDate(project);
1914         TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(editor);
1915         codeAnalyzer.runPasses(file, editor.getDocument(), textEditor, ArrayUtil.EMPTY_INT_ARRAY, true, interrupt);
1916       }
1917       catch (ProcessCanceledException ignored) {
1918         codeAnalyzer.waitForTermination();
1919         continue;
1920       }
1921       fail("PCE must have been thrown");
1922     }
1923   }
1924
1925   public void testCodeFoldingInSplittedWindowAppliesToAllEditors() throws Exception {
1926     final Set<Editor> applied = new THashSet<>();
1927     final Set<Editor> collected = new THashSet<>();
1928     registerFakePass(applied, collected);
1929
1930     configureByText(PlainTextFileType.INSTANCE, "");
1931     Editor editor1 = getEditor();
1932     final Editor editor2 = EditorFactory.getInstance().createEditor(editor1.getDocument(),getProject());
1933     Disposer.register(getProject(), () -> EditorFactory.getInstance().releaseEditor(editor2));
1934     TextEditor textEditor1 = new PsiAwareTextEditorProvider().getTextEditor(editor1);
1935     TextEditor textEditor2 = new PsiAwareTextEditorProvider().getTextEditor(editor2);
1936
1937     List<HighlightInfo> errors = myDaemonCodeAnalyzer.runPasses(myFile, editor1.getDocument(), Arrays.asList(textEditor1,textEditor2), new int[0], false, null);
1938     assertEmpty(errors);
1939
1940     assertEquals(collected, ContainerUtil.newHashSet(editor1, editor2));
1941     assertEquals(applied, ContainerUtil.newHashSet(editor1, editor2));
1942   }
1943
1944   private void registerFakePass(@NotNull final Set<Editor> applied, @NotNull final Set<Editor> collected) {
1945     class Fac extends AbstractProjectComponent implements TextEditorHighlightingPassFactory {
1946       private Fac(Project project) {
1947         super(project);
1948       }
1949
1950       @Override
1951       public TextEditorHighlightingPass createHighlightingPass(@NotNull PsiFile file, @NotNull final Editor editor) {
1952         return new EditorBoundHighlightingPass(editor, file, false) {
1953           @Override
1954           public void doCollectInformation(@NotNull ProgressIndicator progress) {
1955             collected.add(editor);
1956           }
1957
1958           @Override
1959           public void doApplyInformationToEditor() {
1960             applied.add(editor);
1961           }
1962         };
1963       }
1964     }
1965     TextEditorHighlightingPassRegistrar registrar = TextEditorHighlightingPassRegistrar.getInstance(getProject());
1966     registrar.registerTextEditorHighlightingPass(new Fac(getProject()), null, null, false, -1);
1967   }
1968
1969   private volatile boolean runHeavyProcessing;
1970   public void testDaemonDisablesItselfDuringHeavyProcessing() throws Exception {
1971     runHeavyProcessing = false;
1972     DaemonCodeAnalyzerSettings settings = DaemonCodeAnalyzerSettings.getInstance();
1973     int delay = settings.AUTOREPARSE_DELAY;
1974     settings.AUTOREPARSE_DELAY = 0;
1975
1976     final Set<Editor> applied = Collections.synchronizedSet(new THashSet<>());
1977     final Set<Editor> collected = Collections.synchronizedSet(new THashSet<>());
1978     registerFakePass(applied, collected);
1979
1980     configureByText(PlainTextFileType.INSTANCE, "");
1981     Editor editor = getEditor();
1982     EditorTracker editorTracker = getProject().getComponent(EditorTracker.class);
1983     editorTracker.setActiveEditors(Collections.singletonList(editor));
1984     while (HeavyProcessLatch.INSTANCE.isRunning()) {
1985       UIUtil.dispatchAllInvocationEvents();
1986     }
1987     type("xxx"); // restart daemon
1988     assertTrue(editorTracker.getActiveEditors().contains(editor));
1989     assertSame(editor, FileEditorManager.getInstance(myProject).getSelectedTextEditor());
1990
1991     try {
1992       // wait for first pass to complete
1993       long start = System.currentTimeMillis();
1994       while (myDaemonCodeAnalyzer.isRunning() || !applied.contains(editor)) {
1995         UIUtil.dispatchAllInvocationEvents();
1996         if (System.currentTimeMillis() - start > 10000) {
1997           fail("Too long waiting for daemon");
1998         }
1999       }
2000
2001       runHeavyProcessing = true;
2002       ApplicationManager.getApplication().executeOnPooledThread(() -> {
2003         AccessToken token = HeavyProcessLatch.INSTANCE.processStarted("my own heavy op");
2004         try {
2005           while (runHeavyProcessing) {
2006           }
2007         }
2008         finally {
2009           token.finish();
2010         }
2011       });
2012       while (!HeavyProcessLatch.INSTANCE.isRunning()) {
2013         UIUtil.dispatchAllInvocationEvents();
2014       }
2015       applied.clear();
2016       collected.clear();
2017
2018       type("xxx"); // try to restart daemon
2019
2020       start = System.currentTimeMillis();
2021       while (System.currentTimeMillis() < start + 5000) {
2022         assertEmpty(applied);  // it should not restart
2023         assertEmpty(collected);
2024         UIUtil.dispatchAllInvocationEvents();
2025       }
2026     }
2027     finally {
2028       runHeavyProcessing = false;
2029       settings.AUTOREPARSE_DELAY = delay;
2030     }
2031   }
2032
2033   
2034   public void testModificationInsideCodeBlockDoesNotRehighlightWholeFile() throws Exception {
2035     configureByText(JavaFileType.INSTANCE, "class X { int f = \"error\"; int f() { int gg<caret> = 11; return 0;} }");
2036     List<HighlightInfo> errors = highlightErrors();
2037     assertEquals(1, errors.size());
2038     assertEquals("Incompatible types. Found: 'java.lang.String', required: 'int'", errors.get(0).getDescription());
2039
2040     errors.get(0).highlighter.dispose();
2041
2042     errors = highlightErrors();
2043     assertEmpty(errors);
2044
2045     type("23");
2046     errors = highlightErrors();
2047     assertEmpty(errors);
2048
2049     myEditor.getCaretModel().moveToOffset(0);
2050     type("/* */");
2051     errors = highlightErrors();
2052     assertEquals(1, errors.size());
2053     assertEquals("Incompatible types. Found: 'java.lang.String', required: 'int'", errors.get(0).getDescription());
2054   }
2055
2056   public void _testCaretMovementDoesNotRestartHighlighting() throws Exception {
2057     configureByText(JavaFileType.INSTANCE, "class X { int f = \"error\"; int f() { int gg<caret> = 11; return 0;} }");
2058
2059     TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(getEditor());
2060     final DaemonCodeAnalyzerImpl di = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
2061     final AtomicReference<ProgressIndicator> indicator = new AtomicReference<>();
2062     final List<HighlightInfo> errors = filter(
2063       di.runPasses(getFile(), getEditor().getDocument(), textEditor, ArrayUtil.EMPTY_INT_ARRAY, false, () -> {
2064         if (indicator.get() == null) {
2065           indicator.set(di.getUpdateProgress());
2066         }
2067         assertSame(indicator.get(), di.getUpdateProgress());
2068         caretRight();
2069         if (getEditor().getCaretModel().getOffset() == getEditor().getDocument().getTextLength()-1) {
2070           getEditor().getCaretModel().moveToOffset(0);
2071         }
2072       }), HighlightSeverity.ERROR);
2073
2074     assertEquals(1, errors.size());
2075     assertEquals("Incompatible types. Found: 'java.lang.String', required: 'int'", errors.get(0).getDescription());
2076   }
2077
2078   
2079   public void testHighlightingDoesWaitForEmbarrassinglySlowExternalAnnotatorsToFinish() throws Exception {
2080     configureByText(JavaFileType.INSTANCE, "class X { int f() { int gg<caret> = 11; return 0;} }");
2081     final AtomicBoolean run = new AtomicBoolean();
2082     final int SLEEP = 20000;
2083     ExternalAnnotator<Integer, Integer> annotator = new ExternalAnnotator<Integer, Integer>() {
2084       @Nullable
2085       @Override
2086       public Integer collectInformation(@NotNull PsiFile file) {
2087         return 0;
2088       }
2089
2090       @Nullable
2091       @Override
2092       public Integer doAnnotate(final Integer collectedInfo) {
2093         TimeoutUtil.sleep(SLEEP);
2094         return 0;
2095       }
2096
2097       @Override
2098       public void apply(@NotNull final PsiFile file, final Integer annotationResult, @NotNull final AnnotationHolder holder) {
2099         run.set(true);
2100       }
2101     };
2102     ExternalLanguageAnnotators.INSTANCE.addExplicitExtension(JavaLanguage.INSTANCE, annotator);
2103
2104     try {
2105       long start = System.currentTimeMillis();
2106       List<HighlightInfo> errors = filter(CodeInsightTestFixtureImpl.instantiateAndRun(getFile(), getEditor(), new int[0], false),
2107                                           HighlightSeverity.ERROR);
2108       long elapsed = System.currentTimeMillis() - start;
2109
2110       assertEquals(0, errors.size());
2111       assertTrue("Elapsed: "+elapsed, elapsed >= SLEEP);
2112       assertTrue(run.get());
2113     }
2114     finally {
2115       ExternalLanguageAnnotators.INSTANCE.removeExplicitExtension(JavaLanguage.INSTANCE, annotator);
2116     }
2117   }
2118
2119   public void testModificationInExcludedFileDoesNotCauseRehighlight() throws Exception {
2120     final PsiFile excluded = configureByText(JavaFileType.INSTANCE, "class EEE { void f(){} }");
2121     PsiTestUtil.addExcludedRoot(myModule, excluded.getVirtualFile().getParent());
2122
2123     configureByText(JavaFileType.INSTANCE, "class X { <caret> }");
2124     List<HighlightInfo> errors = highlightErrors();
2125     assertEmpty(errors);
2126     FileStatusMap me = DaemonCodeAnalyzerEx.getInstanceEx(getProject()).getFileStatusMap();
2127     TextRange scope = me.getFileDirtyScope(getEditor().getDocument(), Pass.UPDATE_ALL);
2128     assertNull(scope);
2129
2130     WriteCommandAction.runWriteCommandAction(getProject(), () -> ((PsiJavaFile)excluded).getClasses()[0].getMethods()[0].delete());
2131
2132     UIUtil.dispatchAllInvocationEvents();
2133     scope = me.getFileDirtyScope(getEditor().getDocument(), Pass.UPDATE_ALL);
2134     assertNull(scope);
2135   }
2136
2137   public void testModificationInWorkspaceXmlDoesNotCauseRehighlight() throws Exception {
2138     configureByText(JavaFileType.INSTANCE, "class X { <caret> }");
2139     ApplicationEx application = ApplicationManagerEx.getApplicationEx();
2140     boolean appSave = application.isDoNotSave();
2141     application.doNotSave(false);
2142     try {
2143       application.saveAll();
2144       final PsiFile excluded = PsiManager.getInstance(getProject()).findFile(getProject().getWorkspaceFile());
2145
2146       List<HighlightInfo> errors = highlightErrors();
2147       assertEmpty(errors);
2148       FileStatusMap me = DaemonCodeAnalyzerEx.getInstanceEx(getProject()).getFileStatusMap();
2149       TextRange scope = me.getFileDirtyScope(getEditor().getDocument(), Pass.UPDATE_ALL);
2150       assertNull(scope);
2151
2152       WriteCommandAction.runWriteCommandAction(getProject(), () -> {
2153         Document document = PsiDocumentManager.getInstance(getProject()).getDocument(excluded);
2154         document.insertString(0, "<!-- dsfsd -->");
2155         PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
2156       });
2157       UIUtil.dispatchAllInvocationEvents();
2158       scope = me.getFileDirtyScope(getEditor().getDocument(), Pass.UPDATE_ALL);
2159       assertNull(scope);
2160     }
2161     finally {
2162       application.doNotSave(appSave);
2163     }
2164   }
2165
2166   public void testLightBulbDoesNotUpdateIntentionsInEDT() throws Exception {
2167     final IntentionAction longLongUpdate = new AbstractIntentionAction() {
2168       @Override
2169       public void invoke(@NotNull Project project, Editor editor, PsiFile file) {
2170       }
2171
2172       @Nls
2173       @NotNull
2174       @Override
2175       public String getText() {
2176         return "LongAction";
2177       }
2178
2179       @Override
2180       public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
2181         if (ApplicationManager.getApplication().isDispatchThread()) {
2182           throw new RuntimeException("Must not update actions in EDT");
2183         }
2184         return true;
2185       }
2186     };
2187     IntentionManager.getInstance().addAction(longLongUpdate);
2188     Disposer.register(myTestRootDisposable, () -> IntentionManager.getInstance().unregisterIntention(longLongUpdate));
2189     configureByText(JavaFileType.INSTANCE, "class X { <caret>  }");
2190     makeEditorWindowVisible(new Point(0, 0));
2191     doHighlighting();
2192     myDaemonCodeAnalyzer.restart();
2193     DaemonCodeAnalyzerSettings mySettings = DaemonCodeAnalyzerSettings.getInstance();
2194     mySettings.AUTOREPARSE_DELAY = 0;
2195     for (int i=0; i<1000; i++) {
2196       caretRight();
2197       UIUtil.dispatchAllInvocationEvents();
2198       caretLeft();
2199       DaemonProgressIndicator updateProgress = myDaemonCodeAnalyzer.getUpdateProgress();
2200       while(myDaemonCodeAnalyzer.getUpdateProgress() == updateProgress) { // wait until daemon started
2201         UIUtil.dispatchAllInvocationEvents();
2202       }
2203       long start = System.currentTimeMillis();
2204       while (myDaemonCodeAnalyzer.isRunning() && System.currentTimeMillis() < start + 500) {
2205         UIUtil.dispatchAllInvocationEvents(); // wait for a bit more until ShowIntentionsPass.doApplyInformationToEditor() called
2206       }
2207     }
2208   }
2209
2210   public void testLightBulbIsHiddenWhenFixRangeIsCollapsed() {
2211     configureByText(StdFileTypes.JAVA, "class S { void foo() { boolean var; if (<selection>va<caret>r</selection>) {}} }");
2212     ((EditorImpl)myEditor).getScrollPane().getViewport().setSize(1000, 1000);
2213
2214     final Set<LightweightHint> visibleHints = ContainerUtil.newIdentityTroveSet();
2215     getProject().getMessageBus().connect(myTestRootDisposable).subscribe(EditorHintListener.TOPIC, new EditorHintListener() {
2216       @Override
2217       public void hintShown(final Project project, final LightweightHint hint, final int flags) {
2218         visibleHints.add(hint);
2219         hint.addHintListener(new HintListener() {
2220           @Override
2221           public void hintHidden(EventObject event) {
2222             visibleHints.remove(hint);
2223             hint.removeHintListener(this);
2224           }
2225         });
2226       }
2227     });
2228
2229     highlightErrors();
2230     IntentionHintComponent lastHintBeforeDeletion = myDaemonCodeAnalyzer.getLastIntentionHint();
2231     assertNotNull(lastHintBeforeDeletion);
2232
2233     delete(myEditor);
2234     highlightErrors();
2235     IntentionHintComponent lastHintAfterDeletion = myDaemonCodeAnalyzer.getLastIntentionHint();
2236     assertSame(lastHintBeforeDeletion, lastHintAfterDeletion);
2237
2238     assertEmpty(visibleHints);
2239   }
2240   
2241   public void testCodeFoldingPassRestartsOnRegionUnfolding() throws Exception {
2242     DaemonCodeAnalyzerSettings settings = DaemonCodeAnalyzerSettings.getInstance();
2243     int savedDelay = settings.AUTOREPARSE_DELAY;
2244     settings.AUTOREPARSE_DELAY = 0;
2245     try {
2246       configureByText(StdFileTypes.JAVA, "class Foo {\n" +
2247                                          "    void m() {\n" +
2248                                          "\n" +
2249                                          "    }\n" +
2250                                          "}");
2251       CodeFoldingManager.getInstance(getProject()).buildInitialFoldings(myEditor);
2252       waitForDaemon();
2253       EditorTestUtil.executeAction(myEditor, IdeActions.ACTION_COLLAPSE_ALL_REGIONS);
2254       waitForDaemon();
2255       checkFoldingState("[FoldRegion +(25:33), placeholder='{...}']");
2256
2257       new WriteCommandAction<Void>(myProject){
2258         @Override
2259         protected void run(@NotNull Result<Void> result) throws Throwable {
2260           myEditor.getDocument().insertString(0, "/*");
2261         }
2262       }.execute();
2263       waitForDaemon();
2264       checkFoldingState("[FoldRegion -(0:37), placeholder='/.../', FoldRegion +(27:35), placeholder='{...}']");
2265       
2266       EditorTestUtil.executeAction(myEditor, IdeActions.ACTION_EXPAND_ALL_REGIONS);
2267       waitForDaemon();
2268       checkFoldingState("[FoldRegion -(0:37), placeholder='/.../']");
2269     }
2270     finally {
2271       settings.AUTOREPARSE_DELAY = savedDelay;
2272     }
2273   }
2274
2275   private void checkFoldingState(String expected) {
2276     assertEquals(expected, Arrays.toString(myEditor.getFoldingModel().getAllFoldRegions()));
2277   }
2278
2279   private void waitForDaemon() {
2280     long deadline = System.currentTimeMillis() + 60_000;
2281     while (!daemonIsWorkingOrPending()) {
2282       if (System.currentTimeMillis() > deadline) fail("Too long waiting for daemon to start");
2283       UIUtil.dispatchInvocationEvent();
2284     }
2285     while (daemonIsWorkingOrPending()) {
2286       if (System.currentTimeMillis() > deadline) fail("Too long waiting for daemon to finish");
2287       UIUtil.dispatchInvocationEvent();
2288     }
2289   }
2290   
2291   private boolean daemonIsWorkingOrPending() {
2292     return PsiDocumentManager.getInstance(myProject).isUncommited(myEditor.getDocument()) || myDaemonCodeAnalyzer.isRunningOrPending();
2293   }
2294
2295   public void testRehighlightInDebuggerExpressionFragment() throws Exception {
2296     PsiExpressionCodeFragment fragment = JavaCodeFragmentFactory.getInstance(getProject()).createExpressionCodeFragment("+ <caret>\"a\"", null,
2297                                     PsiType.getJavaLangObject(getPsiManager(), GlobalSearchScope.allScope(getProject())), true);
2298     myFile = fragment;
2299     Document document = PsiDocumentManager.getInstance(getProject()).getDocument(fragment);
2300     myEditor = EditorFactory.getInstance().createEditor(document, getProject(), StdFileTypes.JAVA, false);
2301
2302     ProperTextRange visibleRange = makeEditorWindowVisible(new Point(0, 0));
2303     assertEquals(document.getTextLength(), visibleRange.getLength());
2304
2305     try {
2306       final EditorInfo editorInfo = new EditorInfo(document.getText());
2307
2308       final String newFileText = editorInfo.getNewFileText();
2309       ApplicationManager.getApplication().runWriteAction(() -> {
2310         if (!document.getText().equals(newFileText)) {
2311           document.setText(newFileText);
2312         }
2313
2314         editorInfo.applyToEditor(myEditor);
2315       });
2316
2317       PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
2318
2319
2320       List<HighlightInfo> errors = highlightErrors();
2321       HighlightInfo error = assertOneElement(errors);
2322       assertEquals("Operator '+' cannot be applied to 'java.lang.String'", error.getDescription());
2323
2324       type(" ");
2325
2326       Collection<HighlightInfo> afterTyping = highlightErrors();
2327       HighlightInfo after = assertOneElement(afterTyping);
2328       assertEquals("Operator '+' cannot be applied to 'java.lang.String'", after.getDescription());
2329     }
2330     finally {
2331       EditorFactory.getInstance().releaseEditor(myEditor);
2332     }
2333   }
2334
2335   public void testFileReload() throws Exception {
2336     VirtualFile file = createFile("a.java", "").getVirtualFile();
2337     Document document = getDocument(file);
2338     assertNotNull(document);
2339
2340     FileStatusMap fileStatusMap = myDaemonCodeAnalyzer.getFileStatusMap();
2341
2342     WriteCommandAction.runWriteCommandAction(getProject(), () -> {
2343       PlatformTestUtil.tryGcSoftlyReachableObjects();
2344       assertNull(PsiDocumentManager.getInstance(getProject()).getCachedPsiFile(document));
2345
2346       document.insertString(0, "class X { void foo() {}}");
2347       assertEquals(TextRange.from(0, document.getTextLength()), fileStatusMap.getFileDirtyScope(document, Pass.UPDATE_ALL));
2348
2349       FileContentUtilCore.reparseFiles(file);
2350       assertEquals(TextRange.from(0, document.getTextLength()), fileStatusMap.getFileDirtyScope(document, Pass.UPDATE_ALL));
2351
2352       findClass("X").getMethods()[0].delete();
2353       assertEquals(TextRange.from(0, document.getTextLength()), fileStatusMap.getFileDirtyScope(document, Pass.UPDATE_ALL));
2354     });
2355   }
2356 }
2357