2 * Copyright 2000-2016 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.intellij.codeInsight.daemon.impl;
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.hint.EditorHintListener;
26 import com.intellij.codeInsight.intention.AbstractIntentionAction;
27 import com.intellij.codeInsight.intention.IntentionAction;
28 import com.intellij.codeInsight.intention.IntentionManager;
29 import com.intellij.codeInsight.intention.impl.IntentionHintComponent;
30 import com.intellij.codeInspection.InspectionProfile;
31 import com.intellij.codeInspection.LocalInspectionTool;
32 import com.intellij.codeInspection.ProblemsHolder;
33 import com.intellij.codeInspection.accessStaticViaInstance.AccessStaticViaInstance;
34 import com.intellij.codeInspection.deadCode.UnusedDeclarationInspection;
35 import com.intellij.codeInspection.deadCode.UnusedDeclarationInspectionBase;
36 import com.intellij.codeInspection.ex.InspectionToolRegistrar;
37 import com.intellij.codeInspection.ex.InspectionToolWrapper;
38 import com.intellij.codeInspection.ex.LocalInspectionToolWrapper;
39 import com.intellij.codeInspection.htmlInspections.RequiredAttributesInspectionBase;
40 import com.intellij.codeInspection.varScopeCanBeNarrowed.FieldCanBeLocalInspection;
41 import com.intellij.diagnostic.PerformanceWatcher;
42 import com.intellij.diagnostic.ThreadDumper;
43 import com.intellij.execution.filters.TextConsoleBuilderFactory;
44 import com.intellij.execution.ui.ConsoleView;
45 import com.intellij.execution.ui.ConsoleViewContentType;
46 import com.intellij.ide.DataManager;
47 import com.intellij.ide.GeneralSettings;
48 import com.intellij.ide.SaveAndSyncHandlerImpl;
49 import com.intellij.ide.highlighter.JavaFileType;
50 import com.intellij.javaee.ExternalResourceManagerExImpl;
51 import com.intellij.lang.CompositeLanguage;
52 import com.intellij.lang.ExternalLanguageAnnotators;
53 import com.intellij.lang.LanguageFilter;
54 import com.intellij.lang.StdLanguages;
55 import com.intellij.lang.annotation.AnnotationHolder;
56 import com.intellij.lang.annotation.ExternalAnnotator;
57 import com.intellij.lang.annotation.HighlightSeverity;
58 import com.intellij.lang.java.JavaLanguage;
59 import com.intellij.openapi.Disposable;
60 import com.intellij.openapi.actionSystem.CommonDataKeys;
61 import com.intellij.openapi.actionSystem.DataContext;
62 import com.intellij.openapi.actionSystem.IdeActions;
63 import com.intellij.openapi.actionSystem.impl.SimpleDataContext;
64 import com.intellij.openapi.application.AccessToken;
65 import com.intellij.openapi.application.ApplicationManager;
66 import com.intellij.openapi.application.Result;
67 import com.intellij.openapi.application.WriteAction;
68 import com.intellij.openapi.application.ex.ApplicationEx;
69 import com.intellij.openapi.application.ex.ApplicationManagerEx;
70 import com.intellij.openapi.command.CommandProcessor;
71 import com.intellij.openapi.command.WriteCommandAction;
72 import com.intellij.openapi.command.undo.UndoManager;
73 import com.intellij.openapi.components.AbstractProjectComponent;
74 import com.intellij.openapi.components.impl.stores.StorageUtil;
75 import com.intellij.openapi.editor.*;
76 import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
77 import com.intellij.openapi.editor.actionSystem.EditorActionManager;
78 import com.intellij.openapi.editor.actionSystem.TypedAction;
79 import com.intellij.openapi.editor.ex.EditorEx;
80 import com.intellij.openapi.editor.ex.MarkupModelEx;
81 import com.intellij.openapi.editor.ex.RangeHighlighterEx;
82 import com.intellij.openapi.editor.impl.DocumentMarkupModel;
83 import com.intellij.openapi.editor.impl.EditorImpl;
84 import com.intellij.openapi.editor.impl.event.MarkupModelListener;
85 import com.intellij.openapi.editor.markup.GutterIconRenderer;
86 import com.intellij.openapi.editor.markup.HighlighterTargetArea;
87 import com.intellij.openapi.editor.markup.MarkupModel;
88 import com.intellij.openapi.editor.markup.RangeHighlighter;
89 import com.intellij.openapi.fileEditor.FileEditorManager;
90 import com.intellij.openapi.fileEditor.OpenFileDescriptor;
91 import com.intellij.openapi.fileEditor.TextEditor;
92 import com.intellij.openapi.fileEditor.impl.text.PsiAwareTextEditorProvider;
93 import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider;
94 import com.intellij.openapi.fileTypes.PlainTextFileType;
95 import com.intellij.openapi.fileTypes.StdFileTypes;
96 import com.intellij.openapi.module.Module;
97 import com.intellij.openapi.progress.ProcessCanceledException;
98 import com.intellij.openapi.progress.ProgressIndicator;
99 import com.intellij.openapi.progress.impl.CoreProgressManager;
100 import com.intellij.openapi.project.Project;
101 import com.intellij.openapi.project.ProjectManager;
102 import com.intellij.openapi.project.ex.ProjectManagerEx;
103 import com.intellij.openapi.project.impl.ProjectManagerImpl;
104 import com.intellij.openapi.util.Disposer;
105 import com.intellij.openapi.util.ProperTextRange;
106 import com.intellij.openapi.util.Segment;
107 import com.intellij.openapi.util.TextRange;
108 import com.intellij.openapi.util.text.StringUtil;
109 import com.intellij.openapi.vfs.VirtualFile;
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.intellij.lang.annotations.Language;
127 import org.jetbrains.annotations.Nls;
128 import org.jetbrains.annotations.NonNls;
129 import org.jetbrains.annotations.NotNull;
130 import org.jetbrains.annotations.Nullable;
134 import java.io.IOException;
135 import java.lang.reflect.Method;
137 import java.util.List;
138 import java.util.concurrent.atomic.AtomicBoolean;
139 import java.util.concurrent.atomic.AtomicLong;
140 import java.util.concurrent.atomic.AtomicReference;
145 @SuppressWarnings("StringConcatenationInsideStringBufferAppend")
147 public class DaemonRespondToChangesTest extends DaemonAnalyzerTestCase {
148 private static final String BASE_PATH = "/codeInsight/daemonCodeAnalyzer/typing/";
150 private DaemonCodeAnalyzerImpl myDaemonCodeAnalyzer;
153 protected void setUp() throws Exception {
155 enableInspectionTool(new UnusedDeclarationInspection());
156 myDaemonCodeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
157 UndoManager.getInstance(myProject);
158 myDaemonCodeAnalyzer.setUpdateByTimerEnabled(true);
159 DaemonProgressIndicator.setDebug(true);
163 protected void tearDown() throws Exception {
165 Project project = getProject();
166 if (project != null) {
167 doPostponedFormatting(project);
168 ((ProjectManagerImpl)ProjectManagerEx.getInstanceEx()).closeProject(project, false, false, false);
177 protected boolean doTestLineMarkers() {
182 protected void setUpProject() throws Exception {
183 super.setUpProject();
184 ProjectManagerEx.getInstanceEx().openProject(getProject());
185 super.runStartupActivities();
186 UIUtil.dispatchAllInvocationEvents(); // startup activities
190 protected void runStartupActivities() {
194 private static void typeInAlienEditor(Editor alienEditor, char c) {
195 TypedAction action = EditorActionManager.getInstance().getTypedAction();
196 DataContext dataContext = ((EditorEx)alienEditor).getDataContext();
198 action.actionPerformed(alienEditor, c, dataContext);
202 public void testHighlightersUpdate() throws Exception {
203 configureByFile(BASE_PATH + "HighlightersUpdate.java");
204 Document document = getDocument(getFile());
206 List<HighlightInfo> errors = DaemonCodeAnalyzerImpl.getHighlights(document, HighlightSeverity.ERROR, getProject());
207 assertEquals(1, errors.size());
208 TextRange dirty = myDaemonCodeAnalyzer.getFileStatusMap().getFileDirtyScope(document, Pass.UPDATE_ALL);
212 PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
213 dirty = myDaemonCodeAnalyzer.getFileStatusMap().getFileDirtyScope(document, Pass.UPDATE_ALL);
214 assertNotNull(dirty);
218 public void testNoPsiEventsAltogether() throws Exception {
219 configureByFile(BASE_PATH + "HighlightersUpdate.java");
220 Document document = getDocument(getFile());
224 PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
226 TextRange dirty = myDaemonCodeAnalyzer.getFileStatusMap().getFileDirtyScope(document, Pass.UPDATE_ALL);
227 assertEquals(getFile().getTextRange(), dirty); // have to rehighlight whole file in case no PSI events have come
230 public void testRenameClass() throws Exception {
231 configureByFile(BASE_PATH + "AClass.java");
232 Document document = getDocument(getFile());
233 Collection<HighlightInfo> infos = highlightErrors();
234 assertEquals(0, infos.size());
235 final PsiClass psiClass = ((PsiJavaFile)getFile()).getClasses()[0];
236 ApplicationManager.getApplication().runWriteAction(() -> {
237 new RenameProcessor(myProject, psiClass, "Class2", false, false).run();
239 TextRange dirty = myDaemonCodeAnalyzer.getFileStatusMap().getFileDirtyScope(document, Pass.UPDATE_ALL);
240 assertEquals(getFile().getTextRange(), dirty);
244 assertTrue(myDaemonCodeAnalyzer.isErrorAnalyzingFinished(getFile()));
248 public void testTypingSpace() throws Exception {
249 configureByFile(BASE_PATH + "AClass.java");
250 Document document = getDocument(getFile());
251 Collection<HighlightInfo> infos = highlightErrors();
252 assertEquals(0, infos.size());
255 PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
256 PsiElement elementAtCaret = myFile.findElementAt(myEditor.getCaretModel().getOffset());
257 assertTrue(elementAtCaret instanceof PsiWhiteSpace);
259 TextRange dirty = myDaemonCodeAnalyzer.getFileStatusMap().getFileDirtyScope(document, Pass.UPDATE_ALL);
260 assertEquals(elementAtCaret.getTextRange(), dirty);
262 assertTrue(myDaemonCodeAnalyzer.isErrorAnalyzingFinished(getFile()));
266 public void testTypingSpaceInsideError() throws Exception {
267 configureByFile(BASE_PATH + "Error.java");
268 Collection<HighlightInfo> infos = highlightErrors();
269 assertEquals(1, infos.size());
271 for (int i = 0; i < 100; i++) {
273 List<HighlightInfo> errors = highlightErrors();
274 assertEquals(1, errors.size());
279 public void testBackSpaceInsideError() throws Exception {
280 configureByFile(BASE_PATH + "BackError.java");
281 Collection<HighlightInfo> infos = highlightErrors();
282 assertEquals(1, infos.size());
285 List<HighlightInfo> errors = highlightErrors();
286 assertEquals(1, errors.size());
290 protected LocalInspectionTool[] configureLocalInspectionTools() {
291 if (isPerformanceTest() && !getTestName(false).equals("TypingCurliesClearsEndOfFileErrorsInPhp_ItIsPerformanceTestBecauseItRunsTooLong")) {
292 // all possible inspections
293 List<InspectionToolWrapper> all = InspectionToolRegistrar.getInstance().createTools();
294 List<LocalInspectionTool> locals = new ArrayList<>();
295 all.stream().filter(tool -> tool instanceof LocalInspectionToolWrapper).forEach(tool -> {
296 LocalInspectionTool e = ((LocalInspectionToolWrapper)tool).getTool();
299 return locals.toArray(new LocalInspectionTool[locals.size()]);
301 return new LocalInspectionTool[]{
302 new FieldCanBeLocalInspection(),
303 new RequiredAttributesInspectionBase(),
304 new CheckDtdReferencesInspection(),
305 new AccessStaticViaInstance(),
310 public void testUnusedFieldUpdate() throws Exception {
311 configureByFile(BASE_PATH + "UnusedField.java");
312 Document document = getDocument(getFile());
313 List<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING);
314 assertEquals(1, infos.size());
315 assertEquals("Private field 'ffff' is never used", infos.get(0).getDescription());
317 type(" foo(ffff++);");
320 List<HighlightInfo> errors = DaemonCodeAnalyzerImpl.getHighlights(document, HighlightSeverity.WARNING, getProject());
321 assertEquals(0, errors.size());
324 public void testUnusedMethodUpdate() throws Exception {
325 configureByText(JavaFileType.INSTANCE, "class X {\n" +
326 " static void ffff() {}\n" +
327 " public static void main(String[] args){\n" +
328 " for (int i=0; i<1000;i++) {\n" +
329 " System.out.println(i);\n" +
330 " <caret>ffff();\n" +
333 enableInspectionTool(new UnusedDeclarationInspection(true));
334 List<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING);
337 PlatformTestUtil.invokeNamedAction(IdeActions.ACTION_COMMENT_LINE);
338 infos = doHighlighting(HighlightSeverity.WARNING);
340 assertEquals(1, infos.size());
341 assertEquals("Method 'ffff()' is never used", infos.get(0).getDescription());
345 public void testAssignedButUnreadFieldUpdate() throws Exception {
346 configureByFile(BASE_PATH + "AssignedButUnreadField.java");
347 List<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING);
348 assertEquals(1, infos.size());
349 assertEquals("Private field 'text' is assigned but never accessed", infos.get(0).getDescription());
352 WriteCommandAction.runWriteCommandAction(getProject(), () -> {
353 EditorModificationUtil.deleteSelectedText(getEditor());
357 List<HighlightInfo> errors = doHighlighting(HighlightSeverity.WARNING);
358 assertEmpty(getFile().getText(), errors);
361 public void testDaemonIgnoresNonPhysicalEditor() throws Exception {
362 configureByFile(BASE_PATH + "AClass.java");
365 EditorFactory editorFactory = EditorFactory.getInstance();
366 final Document consoleDoc = editorFactory.createDocument("my console blah");
367 final Editor consoleEditor = editorFactory.createEditor(consoleDoc);
370 checkDaemonReaction(false, () -> caretRight(consoleEditor));
371 checkDaemonReaction(true, () -> typeInAlienEditor(consoleEditor, 'x'));
372 checkDaemonReaction(true, () -> LightPlatformCodeInsightTestCase.backspace(consoleEditor, getProject()));
375 checkDaemonReaction(true, this::caretRight);
378 editorFactory.releaseEditor(consoleEditor);
383 public void testDaemonIgnoresConsoleActivities() throws Exception {
384 configureByFile(BASE_PATH + "AClass.java");
385 doHighlighting(HighlightSeverity.WARNING);
387 final ConsoleView consoleView = TextConsoleBuilderFactory.getInstance().createBuilder(getProject()).getConsole();
389 consoleView.getComponent(); //create editor
390 consoleView.print("haha", ConsoleViewContentType.NORMAL_OUTPUT);
391 UIUtil.dispatchAllInvocationEvents();
394 checkDaemonReaction(false, () -> {
397 Thread.sleep(300); // *&^ing alarm
399 catch (InterruptedException e) {
402 UIUtil.dispatchAllInvocationEvents(); //flush
404 checkDaemonReaction(false, () -> {
405 consoleView.print("sss", ConsoleViewContentType.NORMAL_OUTPUT);
407 Thread.sleep(300); // *&^ing alarm
409 catch (InterruptedException e) {
412 UIUtil.dispatchAllInvocationEvents(); //flush
414 checkDaemonReaction(false, () -> {
415 consoleView.setOutputPaused(true);
417 Thread.sleep(300); // *&^ing alarm
419 catch (InterruptedException e) {
422 UIUtil.dispatchAllInvocationEvents(); //flush
426 Disposer.dispose(consoleView);
430 private void checkDaemonReaction(boolean mustCancelItself, @NotNull final Runnable action) {
431 PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
433 myDaemonCodeAnalyzer.waitForTermination();
434 TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(getEditor());
436 final AtomicBoolean run = new AtomicBoolean();
437 Disposable disposable = Disposer.newDisposable();
438 final AtomicReference<RuntimeException> stopDaemonReason = new AtomicReference<>();
439 StorageUtil.DEBUG_LOG = "";
440 getProject().getMessageBus().connect(disposable).subscribe(DaemonCodeAnalyzer.DAEMON_EVENT_TOPIC,
441 new DaemonCodeAnalyzer.DaemonListenerAdapter() {
443 public void daemonCancelEventOccurred(@NotNull String reason) {
444 RuntimeException e = new RuntimeException("Some bastard's restarted daemon: " + reason +
445 "\nStorage write log: ----------\n" +
446 StorageUtil.DEBUG_LOG +"\n--------------");
447 stopDaemonReason.compareAndSet(null, e);
453 myDaemonCodeAnalyzer.runPasses(getFile(), getDocument(getFile()), textEditor, new int[0], true, () -> {
454 if (!run.getAndSet(true)) {
460 catch (ProcessCanceledException ignored) { }
463 if (mustCancelItself) {
464 assertNotNull(stopDaemonReason.get());
467 if (stopDaemonReason.get() != null) throw stopDaemonReason.get();
471 StorageUtil.DEBUG_LOG = null;
472 Disposer.dispose(disposable);
477 public void testWholeFileInspection() throws Exception {
478 configureByFile(BASE_PATH + "FieldCanBeLocal.java");
479 List<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING);
480 assertEquals(1, infos.size());
481 assertEquals("Field can be converted to a local variable", infos.get(0).getDescription());
484 WriteCommandAction.runWriteCommandAction(getProject(), () -> {
485 EditorModificationUtil.deleteSelectedText(getEditor());
489 infos = doHighlighting(HighlightSeverity.WARNING);
493 WriteCommandAction.runWriteCommandAction(getProject(), () -> {
494 EditorModificationUtil.deleteSelectedText(getEditor());
498 infos = doHighlighting(HighlightSeverity.WARNING);
499 assertEquals(1, infos.size());
500 assertEquals("Field can be converted to a local variable", infos.get(0).getDescription());
503 private static class MyWholeInspection extends LocalInspectionTool {
504 private final List<PsiElement> visited = Collections.synchronizedList(new ArrayList<>());
509 public String getGroupDisplayName() {
516 public String getDisplayName() {
517 return getGroupDisplayName();
522 public String getShortName() {
523 return getGroupDisplayName();
528 public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
529 return new PsiElementVisitor() {
531 public void visitFile(PsiFile file) {
532 TimeoutUtil.sleep(1000); // make it run longer that LIP
533 super.visitFile(file);
537 public void visitElement(PsiElement element) {
538 visited.add(element);
539 super.visitElement(element);
545 public boolean runForWholeFile() {
550 public void testWholeFileInspectionRestartedOnAllElements() throws Exception {
551 MyWholeInspection tool = new MyWholeInspection();
552 enableInspectionTool(tool);
553 disposeOnTearDown(() -> disableInspectionTool(tool.getShortName()));
555 configureByText(JavaFileType.INSTANCE, "class X { void f() { <caret> } }");
556 List<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING);
558 int visitedCount = new HashSet<>(tool.visited).size();
559 tool.visited.clear();
561 type(" "); // white space modification
563 infos = doHighlighting(HighlightSeverity.WARNING);
566 int countAfter = new HashSet<>(tool.visited).size();
567 assertTrue("visitedCount = "+visitedCount+"; countAfter="+countAfter, countAfter >= visitedCount);
570 public void testWholeFileInspectionRestartedEvenIfThereWasAModificationInsideCodeBlockInOtherFile() throws Exception {
571 MyWholeInspection tool = new MyWholeInspection();
573 enableInspectionTool(tool);
574 disposeOnTearDown(() -> disableInspectionTool(tool.getShortName()));
576 PsiFile file = configureByText(JavaFileType.INSTANCE, "class X { void f() { <caret> } }");
577 PsiFile otherFile = createFile(myModule, file.getContainingDirectory().getVirtualFile(), "otherFile.txt", "xxx");
578 List<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING);
580 int visitedCount = tool.visited.size();
581 assertTrue(tool.visited.toString(), visitedCount > 0);
582 tool.visited.clear();
584 Document otherDocument = PsiDocumentManager.getInstance(getProject()).getDocument(otherFile);
585 WriteCommandAction.runWriteCommandAction(getProject(), () -> otherDocument.setText("zzz"));
587 infos = doHighlighting(HighlightSeverity.WARNING);
590 int countAfter = tool.visited.size();
591 assertTrue(tool.visited.toString(), countAfter > 0);
592 tool.visited.clear();
594 //ensure started on another file
595 configureByExistingFile(otherFile.getVirtualFile());
596 infos = doHighlighting(HighlightSeverity.WARNING);
599 int countAfter2 = tool.visited.size();
600 assertTrue(tool.visited.toString(), countAfter2 > 0);
603 public void testOverriddenMethodMarkers() throws Exception {
604 configureByFile(BASE_PATH + getTestName(false) + ".java");
607 Document document = getEditor().getDocument();
608 List<LineMarkerInfo> markers = DaemonCodeAnalyzerImpl.getLineMarkers(document, getProject());
609 assertEquals(3, markers.size());
614 markers = DaemonCodeAnalyzerImpl.getLineMarkers(document, getProject());
615 assertEquals(3, markers.size());
619 public void testOverriddenMethodMarkersDoNotClearedByChangingWhitespaceNearby() throws Exception {
620 configureByFile(BASE_PATH + "OverriddenMethodMarkers.java");
623 Document document = getEditor().getDocument();
624 List<LineMarkerInfo> markers = DaemonCodeAnalyzerImpl.getLineMarkers(document, getProject());
625 assertEquals(markers.toString(), 3, markers.size());
627 PsiElement element = ((PsiJavaFile)myFile).getClasses()[0].findMethodsByName("f", false)[0].getReturnTypeElement().getNextSibling();
628 assertEquals(" ", element.getText());
629 getEditor().getCaretModel().moveToOffset(element.getTextOffset() + 1);
633 markers = DaemonCodeAnalyzerImpl.getLineMarkers(document, getProject());
634 assertEquals(markers.toString(), 3, markers.size());
638 public void testChangeXmlIncludeLeadsToRehighlight() throws Exception {
639 LanguageFilter[] extensions = ((CompositeLanguage)StdLanguages.XML).getLanguageExtensions();
640 for (LanguageFilter extension : extensions) {
641 ((CompositeLanguage)StdLanguages.XML).unregisterLanguageExtension(extension);
644 final String location = getTestName(false) + ".xsd";
645 final String url = "http://myschema/";
646 ExternalResourceManagerExImpl.registerResourceTemporarily(url, location, getTestRootDisposable());
648 configureByFiles(null, BASE_PATH + getTestName(false) + ".xml", BASE_PATH + getTestName(false) + ".xsd");
650 Collection<HighlightInfo> errors = highlightErrors();
651 assertEquals(0, errors.size());
653 Editor[] allEditors = EditorFactory.getInstance().getAllEditors();
654 Editor schemaEditor = null;
655 for (Editor editor : allEditors) {
656 Document document = editor.getDocument();
657 PsiFile file = PsiDocumentManager.getInstance(getProject()).getPsiFile(document);
658 if (file == null) continue;
659 if (location.equals(file.getName())) {
660 schemaEditor = editor;
664 delete(schemaEditor);
666 errors = highlightErrors();
667 assertFalse(errors.isEmpty());
669 for (LanguageFilter extension : extensions) {
670 ((CompositeLanguage)StdLanguages.XML).registerLanguageExtension(extension);
675 public void testRehighlightInnerBlockAfterInline() throws Exception {
676 configureByFile(BASE_PATH + getTestName(false) + ".java");
678 Collection<HighlightInfo> errors = highlightErrors();
679 HighlightInfo error = assertOneElement(errors);
680 assertEquals("Variable 'e' is already defined in the scope", error.getDescription());
681 PsiElement element = getFile().findElementAt(getEditor().getCaretModel().getOffset()).getParent();
683 DataContext dataContext = SimpleDataContext.getSimpleContext(CommonDataKeys.PSI_ELEMENT.getName(), element, ((EditorEx)getEditor()).getDataContext());
684 new InlineRefactoringActionHandler().invoke(getProject(), getEditor(), getFile(), dataContext);
686 Collection<HighlightInfo> afterTyping = highlightErrors();
687 assertEmpty(afterTyping);
691 public void testRangeMarkersDoNotGetAddedOrRemovedWhenUserIsJustTypingInsideHighlightedRegionAndEspeciallyInsideInjectedFragmentsWhichAreColoredGreenAndUsersComplainEndlesslyThatEditorFlickersThere()
693 configureByText(JavaFileType.INSTANCE, "class S { int f() {\n" +
694 " return <caret>hashCode();\n" +
697 Collection<HighlightInfo> infos = doHighlighting(HighlightInfoType.SYMBOL_TYPE_SEVERITY);
698 assertEquals(3, infos.size());
700 final int[] count = {0};
701 MarkupModelEx modelEx = (MarkupModelEx)DocumentMarkupModel.forDocument(getDocument(getFile()), getProject(), true);
702 modelEx.addMarkupModelListener(getTestRootDisposable(), new MarkupModelListener.Adapter() {
704 public void afterAdded(@NotNull RangeHighlighterEx highlighter) {
709 public void beforeRemoved(@NotNull RangeHighlighterEx highlighter) {
717 assertEquals(0, count[0]);
720 public void testLineMarkersReuse() throws Throwable {
721 configureByFile(BASE_PATH + "LineMarkerChange.java");
723 List<HighlightInfo> errors = highlightErrors();
726 List<LineMarkerInfo> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
727 assertSize(5, lineMarkers);
731 final Collection<String> changed = new ArrayList<>();
732 MarkupModelEx modelEx = (MarkupModelEx)DocumentMarkupModel.forDocument(getDocument(getFile()), getProject(), true);
733 modelEx.addMarkupModelListener(getTestRootDisposable(), new MarkupModelListener() {
735 public void afterAdded(@NotNull RangeHighlighterEx highlighter) {
736 changed(highlighter, ExceptionUtil.getThrowableText(new Throwable("after added")));
740 public void beforeRemoved(@NotNull RangeHighlighterEx highlighter) {
741 changed(highlighter, ExceptionUtil.getThrowableText(new Throwable("before removed")));
745 public void attributesChanged(@NotNull RangeHighlighterEx highlighter, boolean renderersChanged, boolean fontStyleChanged) {
746 changed(highlighter, ExceptionUtil.getThrowableText(new Throwable("changed")));
749 private void changed(@NotNull RangeHighlighterEx highlighter, String reason) {
750 if (highlighter.getTargetArea() != HighlighterTargetArea.LINES_IN_RANGE) return; // not line marker
751 List<LineMarkerInfo> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
752 if (ContainerUtil.find(lineMarkers, lm -> lm.highlighter == highlighter) == null) return; // not line marker
754 changed.add(highlighter+": \n"+reason);
758 PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
759 List<HighlightInfo> infosAfter = CodeInsightTestFixtureImpl.instantiateAndRun(myFile, myEditor, new int[]{/*Pass.UPDATE_ALL, Pass.LOCAL_INSPECTIONS*/}, false);
760 assertNotEmpty(filter(infosAfter, HighlightSeverity.ERROR));
762 assertEmpty(changed);
763 List<LineMarkerInfo> lineMarkersAfter = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
764 assertEquals(lineMarkersAfter.size(), lineMarkers.size());
767 public void testLineMarkersDoNotBlinkOnBackSpaceRightBeforeMethodIdentifier() throws Throwable {
768 configureByText(JavaFileType.INSTANCE, "package x; \n" +
769 "class <caret>ToRun{\n" +
770 " public static void main(String[] args) {\n"+
774 List<HighlightInfo> errors = highlightErrors();
777 List<LineMarkerInfo> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
778 assertSize(2, lineMarkers);
782 final Collection<String> changed = new ArrayList<>();
783 MarkupModelEx modelEx = (MarkupModelEx)DocumentMarkupModel.forDocument(getDocument(getFile()), getProject(), true);
784 modelEx.addMarkupModelListener(getTestRootDisposable(), new MarkupModelListener() {
786 public void afterAdded(@NotNull RangeHighlighterEx highlighter) {
787 changed(highlighter, ExceptionUtil.getThrowableText(new Throwable("after added")));
791 public void beforeRemoved(@NotNull RangeHighlighterEx highlighter) {
792 changed(highlighter, ExceptionUtil.getThrowableText(new Throwable("before removed")));
796 public void attributesChanged(@NotNull RangeHighlighterEx highlighter, boolean renderersChanged, boolean fontStyleChanged) {
797 changed(highlighter, ExceptionUtil.getThrowableText(new Throwable("changed")));
800 private void changed(@NotNull RangeHighlighterEx highlighter, String reason) {
801 if (highlighter.getTargetArea() != HighlighterTargetArea.LINES_IN_RANGE) return; // not line marker
802 List<LineMarkerInfo> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
803 if (ContainerUtil.find(lineMarkers, lm -> lm.highlighter == highlighter) == null) return; // not line marker
805 changed.add(highlighter+": \n"+reason);
809 assertEmpty(highlightErrors());
811 assertSize(2, DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject()));
813 assertEmpty(changed);
816 public void testTypeParametersMustNotBlinkWhenTypingInsideClass() throws Throwable {
817 configureByText(JavaFileType.INSTANCE, "package x; \n" +
818 "abstract class ToRun<TTTTTTTTTTTTTTT> implements Comparable<TTTTTTTTTTTTTTT> {\n" +
819 " private ToRun<TTTTTTTTTTTTTTT> delegate;\n"+
824 List<HighlightInfo> errors = highlightErrors();
827 MarkupModelEx modelEx = (MarkupModelEx)DocumentMarkupModel.forDocument(getDocument(getFile()), getProject(), true);
828 modelEx.addMarkupModelListener(getTestRootDisposable(), new MarkupModelListener.Adapter() {
830 public void beforeRemoved(@NotNull RangeHighlighterEx highlighter) {
831 if (TextRange.create(highlighter).substring(highlighter.getDocument().getText()).equals("TTTTTTTTTTTTTTT")) {
832 throw new RuntimeException("Must not remove type parameter highlighter");
837 assertEmpty(highlightErrors());
840 assertEmpty(highlightErrors());
842 assertEmpty(highlightErrors());
844 assertEmpty(highlightErrors());
846 assertEmpty(highlightErrors());
849 assertEmpty(highlightErrors());
852 public void testLocallyUsedFieldHighlighting() {
853 configureByText(JavaFileType.INSTANCE, "class A {\n" +
856 " String local = null;\n" +
857 " <selection>cons</selection>.substring(1);" +
859 " public static void main(String[] args) {\n" +
860 " new A().foo();\n" +
863 enableInspectionTool(new UnusedDeclarationInspection(true));
865 List<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING);
866 assertSize(1, infos);
867 assertEquals("Variable 'local' is never used", infos.get(0).getDescription());
871 infos = doHighlighting(HighlightSeverity.WARNING);
872 assertSize(1, infos);
873 assertEquals("Field 'cons' is never used", infos.get(0).getDescription());
876 public void testOverrideMethodsHighlightingPersistWhenTypeInsideMethodBody() throws Throwable {
877 configureByText(JavaFileType.INSTANCE, "package x; \n" +
879 " static <T> void sayHello(Class<? extends T> msg) {}\n" +
882 "class ClassB extends ClassA {\n" +
883 " static <T extends String> void sayHello(Class<? extends T> msg) {<caret>\n" +
887 assertSize(1, highlightErrors());
888 type("//my comment inside method body, so class modifier won't be visited");
889 assertSize(1, highlightErrors());
892 public void testLineMarkersClearWhenTypingAtTheEndOfPsiComment() throws Throwable {
893 configureByText(JavaFileType.INSTANCE, "class S {\n//ddd<caret>\n}");
894 StringBuffer log = new StringBuffer();
895 final LineMarkerProvider provider = new LineMarkerProvider() {
898 public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement element) {
899 String msg = "provider.getLineMarkerInfo(" + element + ") called\n";
900 LineMarkerInfo<PsiComment> info = null;
901 if (element instanceof PsiComment) {
902 info = new LineMarkerInfo<>((PsiComment)element, element.getTextRange(), null, Pass.LINE_MARKERS, null, null, GutterIconRenderer.Alignment.LEFT);
903 msg += " provider info: "+info + "\n";
910 public void collectSlowLineMarkers(@NotNull List<PsiElement> elements, @NotNull Collection<LineMarkerInfo> result) {
914 LineMarkerProviders.INSTANCE.addExplicitExtension(JavaLanguage.INSTANCE, provider);
915 Disposer.register(getTestRootDisposable(), () -> LineMarkerProviders.INSTANCE.removeExplicitExtension(JavaLanguage.INSTANCE, provider));
916 myDaemonCodeAnalyzer.restart();
918 TextRange range = FileStatusMap.getDirtyTextRange(getEditor(), Pass.UPDATE_ALL);
919 log.append("FileStatusMap.getDirtyTextRange: " + range+"\n");
920 List<PsiElement> elements = CollectHighlightsUtil.getElementsInRange(getFile(), range.getStartOffset(), range.getEndOffset());
921 log.append("CollectHighlightsUtil.getElementsInRange" + range + ": " + elements.size() +" elements : "+ elements+"\n");
922 List<HighlightInfo> infos = doHighlighting();
923 log.append(" File text: '" + getFile().getText() + "'\n");
924 log.append("infos: " + infos + "\n");
925 assertEmpty(filter(infos,HighlightSeverity.ERROR));
927 List<LineMarkerInfo> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
928 assertOneElement(lineMarkers);
931 infos = doHighlighting();
932 log.append("File text: '" + getFile().getText() + "'\n");
933 log.append("infos: " + infos + "\n");
934 assertEmpty(filter(infos,HighlightSeverity.ERROR));
936 lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
937 assertOneElement(lineMarkers);
940 infos = doHighlighting();
941 log.append("File text: '" + getFile().getText() + "'\n");
942 log.append("infos: " + infos + "\n");
943 assertEmpty(filter(infos,HighlightSeverity.ERROR));
945 lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
946 assertOneElement(lineMarkers);
948 catch (AssertionError e) {
949 System.err.println("Log:\n"+log+"\n---");
954 public void testWhenTypingOverWrongReferenceItsColorChangesToBlackAndOnlyAfterHighlightingFinishedItReturnsToRed() throws Throwable {
955 configureByText(StdFileTypes.JAVA, "class S { int f() {\n" +
956 " return asfsdfsdfsd<caret>;\n" +
959 Collection<HighlightInfo> errors = highlightErrors();
960 assertOneElement(errors);
961 assertSame(HighlightInfoType.WRONG_REF, errors.iterator().next().type);
963 Document document = getDocument(getFile());
967 List<HighlightInfo> infos = DaemonCodeAnalyzerImpl.getHighlights(document, HighlightInfoType.SYMBOL_TYPE_SEVERITY, getProject());
968 for (HighlightInfo info : infos) {
969 assertNotSame(HighlightInfoType.WRONG_REF, info.type);
972 errors = highlightErrors();
973 assertOneElement(errors);
974 assertSame(HighlightInfoType.WRONG_REF, errors.iterator().next().type);
978 public void testQuickFixRemainsAvailableAfterAnotherFixHasBeenAppliedInTheSameCodeBlockBefore() throws Exception {
979 configureByFile(BASE_PATH + "QuickFixes.java");
981 DaemonCodeAnalyzerSettings settings = DaemonCodeAnalyzerSettings.getInstance();
982 boolean old = settings.NEXT_ERROR_ACTION_GOES_TO_ERRORS_FIRST;
983 settings.NEXT_ERROR_ACTION_GOES_TO_ERRORS_FIRST = true;
986 Collection<HighlightInfo> errors = highlightErrors();
987 assertEquals(3, errors.size());
988 new GotoNextErrorHandler(true).invoke(getProject(), getEditor(), getFile());
990 List<IntentionAction> fixes = LightQuickFixTestCase.getAvailableActions(getEditor(), getFile());
991 IntentionAction fix = assertContainsOneOf(fixes, DeleteCatchFix.class);
992 assertEquals("Delete catch for 'java.io.IOException'", fix.getText());
994 final IntentionAction finalFix = fix;
995 WriteCommandAction.runWriteCommandAction(getProject(), () -> finalFix.invoke(getProject(), getEditor(), getFile()));
997 errors = highlightErrors();
998 assertEquals(2, errors.size());
1000 new GotoNextErrorHandler(true).invoke(getProject(), getEditor(), getFile());
1001 fixes = LightQuickFixTestCase.getAvailableActions(getEditor(), getFile());
1002 fix = assertContainsOneOf(fixes, DeleteCatchFix.class);
1003 assertEquals("Delete catch for 'java.io.IOException'", fix.getText());
1005 final IntentionAction finalFix1 = fix;
1006 WriteCommandAction.runWriteCommandAction(getProject(), () -> finalFix1.invoke(getProject(), getEditor(), getFile()));
1008 errors = highlightErrors();
1009 assertOneElement(errors);
1011 new GotoNextErrorHandler(true).invoke(getProject(), getEditor(), getFile());
1012 fixes = LightQuickFixTestCase.getAvailableActions(getEditor(), getFile());
1013 fix = assertContainsOneOf(fixes, DeleteCatchFix.class);
1014 assertEquals("Delete catch for 'java.io.IOException'", fix.getText());
1016 final IntentionAction finalFix2 = fix;
1017 WriteCommandAction.runWriteCommandAction(getProject(), () -> finalFix2.invoke(getProject(), getEditor(), getFile()));
1019 errors = highlightErrors();
1020 assertEmpty(errors);
1023 settings.NEXT_ERROR_ACTION_GOES_TO_ERRORS_FIRST = old;
1027 private static <T> T assertContainsOneOf(@NotNull Collection<T> collection, @NotNull Class<?> aClass) {
1029 for (T t : collection) {
1030 if (aClass.isInstance(t)) {
1031 if (result != null) {
1032 fail("multiple " + aClass.getName() + " objects present in collection " + collection);
1039 assertNotNull(aClass.getName() + " object not found in collection " + collection, result);
1044 public void testRangeHighlightersDoNotGetStuckForever() throws Throwable {
1045 configureByText(StdFileTypes.JAVA, "class S { void ffffff() {fff<caret>fff();}}");
1047 List<HighlightInfo> infos = highlightErrors();
1049 MarkupModel markup = DocumentMarkupModel.forDocument(myEditor.getDocument(), getProject(), true);
1050 final TextRange[] highlightersBefore = getHighlightersTextRange(markup);
1058 infos = highlightErrors();
1061 final TextRange[] highlightersAfter = getHighlightersTextRange(markup);
1063 assertEquals(highlightersBefore.length, highlightersAfter.length);
1064 for (int i = 0; i < highlightersBefore.length; i++) {
1065 TextRange before = highlightersBefore[i];
1066 TextRange after = highlightersAfter[i];
1067 assertEquals(before.getStartOffset(), after.getStartOffset());
1068 assertEquals(before.getEndOffset(), after.getEndOffset());
1073 private static TextRange[] getHighlightersTextRange(@NotNull MarkupModel markup) {
1074 final RangeHighlighter[] highlighters = markup.getAllHighlighters();
1076 final TextRange[] result = new TextRange[highlighters.length];
1077 for (int i = 0; i < highlighters.length; i++) {
1078 result[i] = ProperTextRange.create(highlighters[i]);
1080 return orderByHashCode(result); // markup.getAllHighlighters returns unordered array
1084 private static <T extends Segment> T[] orderByHashCode(@NotNull T[] highlighters) {
1085 Arrays.sort(highlighters, (o1, o2) -> o2.hashCode() - o1.hashCode());
1086 return highlighters;
1089 public void testFileStatusMapDirtyCachingWorks() throws Throwable {
1090 myDaemonCodeAnalyzer.setUpdateByTimerEnabled(false); // to prevent auto-start highlighting
1091 UIUtil.dispatchAllInvocationEvents();
1092 configureByText(StdFileTypes.JAVA, "class <caret>S { int ffffff = 0;}");
1093 UIUtil.dispatchAllInvocationEvents();
1095 final int[] creation = {0};
1096 class Fac extends AbstractProjectComponent implements TextEditorHighlightingPassFactory {
1097 private Fac(Project project) {
1102 public TextEditorHighlightingPass createHighlightingPass(@NotNull PsiFile file, @NotNull Editor editor) {
1103 TextRange textRange = FileStatusMap.getDirtyTextRange(editor, Pass.UPDATE_ALL);
1104 if (textRange == null) return null;
1105 return new MyPass(myProject);
1108 class MyPass extends TextEditorHighlightingPass {
1109 private MyPass(final Project project) {
1110 super(project, getEditor().getDocument(), false);
1115 public void doCollectInformation(@NotNull ProgressIndicator progress) {
1119 public void doApplyInformationToEditor() {
1123 TextEditorHighlightingPassRegistrar registrar = TextEditorHighlightingPassRegistrar.getInstance(getProject());
1124 registrar.registerTextEditorHighlightingPass(new Fac(getProject()), null, null, false, -1);
1126 assertEquals(1, creation[0]);
1130 assertEquals(1, creation[0]);
1132 assertEquals(1, creation[0]);
1136 assertEquals(2, creation[0]);
1138 assertEquals(2, creation[0]);
1140 assertEquals(2, creation[0]);
1144 public void testDefensivelyDirtyFlagDoesNotClearPrematurely() throws Throwable {
1145 class Fac extends AbstractProjectComponent implements TextEditorHighlightingPassFactory {
1146 private Fac(Project project) {
1151 public TextEditorHighlightingPass createHighlightingPass(@NotNull PsiFile file, @NotNull Editor editor) {
1155 TextEditorHighlightingPassRegistrar registrar = TextEditorHighlightingPassRegistrar.getInstance(getProject());
1156 registrar.registerTextEditorHighlightingPass(new Fac(getProject()), null, null, false, -1);
1158 configureByText(StdFileTypes.JAVA, "@Deprecated<caret> class S { } ");
1160 List<HighlightInfo> infos = doHighlighting(HighlightInfoType.SYMBOL_TYPE_SEVERITY);
1161 assertEquals(2, infos.size());
1163 assertEquals("@Deprecated", infos.get(0).getText());
1164 assertEquals("S", infos.get(1).getText());
1169 List<HighlightInfo> after = doHighlighting(HighlightInfoType.SYMBOL_TYPE_SEVERITY);
1171 assertEquals("@Deprecated", after.get(0).getText());
1172 assertEquals("S", after.get(1).getText());
1177 getEditor().getCaretModel().moveToOffset(getEditor().getDocument().getTextLength());
1180 after = doHighlighting(HighlightInfoType.SYMBOL_TYPE_SEVERITY);
1181 assertEquals(2, after.size());
1183 assertEquals("@Deprecated", after.get(0).getText());
1184 assertEquals("S", after.get(1).getText());
1188 public void testModificationInsideCodeblockDoesnotAffectErrorMarkersOutside() throws Exception {
1189 configureByFile(BASE_PATH + "ErrorMark.java");
1190 List<HighlightInfo> errs = highlightErrors();
1191 assertEquals(1, errs.size());
1192 assertEquals("'}' expected", errs.get(0).getDescription());
1195 errs = highlightErrors();
1196 assertEquals(1, errs.size());
1197 assertEquals("'}' expected", errs.get(0).getDescription());
1200 public void testErrorMarkerAtTheEndOfTheFile() throws Exception {
1201 CommandProcessor.getInstance().executeCommand(getProject(), () -> {
1203 configureByFile(BASE_PATH + "ErrorMarkAtEnd.java");
1205 catch (Exception e) {
1209 List<HighlightInfo> errs = highlightErrors();
1211 CommandProcessor.getInstance().executeCommand(getProject(), () -> {
1212 Document document = getEditor().getDocument();
1213 int offset = getEditor().getCaretModel().getOffset();
1214 while (offset < document.getTextLength()) {
1215 int i = StringUtil.indexOf(document.getText(), '}', offset, document.getTextLength());
1217 getEditor().getCaretModel().moveToOffset(i);
1218 delete(getEditor());
1222 errs = highlightErrors();
1223 assertEquals(2, errs.size());
1224 assertEquals("'}' expected", errs.get(0).getDescription());
1227 errs = highlightErrors();
1233 public void _testSOEInEndlessAppendChainPerformance() throws Throwable {
1234 StringBuilder text = new StringBuilder("class S { String ffffff = new StringBuilder()\n");
1235 for (int i=0; i<2000; i++) {
1236 text.append(".append(").append(i).append(")\n");
1238 text.append(".toString();<caret>}");
1239 configureByText(StdFileTypes.JAVA, text.toString());
1241 PlatformTestUtil.startPerformanceTest("too many tree visitors", 30000, () -> {
1242 List<HighlightInfo> infos = highlightErrors();
1245 List<HighlightInfo> errors = highlightErrors();
1246 assertFalse(errors.isEmpty());
1253 infos = highlightErrors();
1255 }).useLegacyScaling().assertTiming();
1259 public void testBulbAppearsAfterType() throws Throwable {
1260 String text = "class S { ArrayList<caret>XXX x;}";
1261 configureByText(StdFileTypes.JAVA, text);
1263 ((EditorImpl)myEditor).getScrollPane().getViewport().setSize(1000, 1000);
1264 DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true);
1266 final Set<LightweightHint> shown = ContainerUtil.newIdentityTroveSet();
1267 getProject().getMessageBus().connect().subscribe(EditorHintListener.TOPIC, (project, hint, flags) -> {
1269 hint.addHintListener(event -> shown.remove(hint));
1272 DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
1275 IntentionHintComponent hintComponent = codeAnalyzer.getLastIntentionHint();
1276 assertNotNull(hintComponent);
1277 assertFalse(hintComponent.isDisposed());
1278 assertNotNull(hintComponent.getComponentHint());
1279 assertTrue(shown.contains(hintComponent.getComponentHint()));
1283 hintComponent = codeAnalyzer.getLastIntentionHint();
1284 assertNotNull(hintComponent);
1285 assertFalse(hintComponent.isDisposed());
1286 assertNotNull(hintComponent.getComponentHint());
1287 assertTrue(shown.contains(hintComponent.getComponentHint()));
1291 protected void configureByExistingFile(@NotNull VirtualFile virtualFile) {
1292 super.configureByExistingFile(virtualFile);
1293 EditorTracker editorTracker = getProject().getComponent(EditorTracker.class);
1294 editorTracker.setActiveEditors(Collections.singletonList(getEditor()));
1298 protected VirtualFile configureByFiles(@Nullable File rawProjectRoot, @NotNull VirtualFile... vFiles) throws IOException {
1299 VirtualFile file = super.configureByFiles(rawProjectRoot, vFiles);
1300 EditorTracker editorTracker = getProject().getComponent(EditorTracker.class);
1301 editorTracker.setActiveEditors(Collections.singletonList(getEditor()));
1305 public void testDaemonIgnoresFrameDeactivation() throws Throwable {
1306 // return default value to avoid unnecessary save
1307 DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true);
1309 String text = "class S { ArrayList<caret>XXX x;}";
1310 configureByText(StdFileTypes.JAVA, text);
1313 GeneralSettings settings = GeneralSettings.getInstance();
1314 ApplicationEx application = ApplicationManagerEx.getApplicationEx();
1315 boolean frameSave = settings.isSaveOnFrameDeactivation();
1316 boolean appSave = application.isDoNotSave();
1318 settings.setSaveOnFrameDeactivation(true);
1319 application.doNotSave(false);
1321 SaveAndSyncHandlerImpl.doSaveDocumentsAndProjectsAndApp();
1323 checkDaemonReaction(false, SaveAndSyncHandlerImpl::doSaveDocumentsAndProjectsAndApp);
1326 application.doNotSave(appSave);
1327 settings.setSaveOnFrameDeactivation(frameSave);
1331 public void testApplyLocalQuickFix() throws Throwable {
1332 configureByText(StdFileTypes.JAVA, "class X { static int sss; public int f() { return this.<caret>sss; }}");
1334 ((EditorImpl)myEditor).getScrollPane().getViewport().setSize(1000, 1000);
1335 DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true);
1337 List<HighlightInfo> warns = doHighlighting(HighlightSeverity.WARNING);
1338 assertOneElement(warns);
1339 List<HighlightInfo.IntentionActionDescriptor> actions = ShowIntentionsPass.getAvailableFixes(getEditor(), getFile(), -1);
1340 final HighlightInfo.IntentionActionDescriptor descriptor = assertOneElement(actions);
1341 WriteCommandAction.runWriteCommandAction(getProject(), () -> descriptor.getAction().invoke(getProject(), getEditor(), getFile()));
1344 actions = ShowIntentionsPass.getAvailableFixes(getEditor(), getFile(), -1);
1345 assertEmpty(actions);
1349 public void testApplyErrorInTheMiddle() throws Throwable {
1350 String text = "class <caret>X { ";
1351 for (int i = 0; i < 100; i++) {
1353 "// String x = \"<zzzzzzzzzz/>\";\n" +
1357 configureByText(StdFileTypes.JAVA, text);
1359 ((EditorImpl)myEditor).getScrollPane().getViewport().setSize(1000, 1000);
1360 DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true);
1362 List<HighlightInfo> warns = highlightErrors();
1366 List<HighlightInfo> errors = highlightErrors();
1367 assertEquals(2, errors.size());
1372 errors = highlightErrors();
1373 assertEmpty(errors);
1377 public void testErrorInTheEndOutsideVisibleArea() throws Throwable {
1378 String text = "<xml> \n" + StringUtil.repeatSymbol('\n', 1000) + "</xml>\nxxxxx<caret>";
1379 configureByText(StdFileTypes.XML, text);
1381 ProperTextRange visibleRange = makeEditorWindowVisible(new Point(0, 1000), myEditor);
1382 assertTrue(visibleRange.getStartOffset() > 0);
1384 List<HighlightInfo> warns = highlightErrors();
1385 HighlightInfo info = assertOneElement(warns);
1386 assertEquals("Top level element is not completed", info.getDescription());
1389 List<HighlightInfo> errors = highlightErrors();
1390 info = assertOneElement(errors);
1391 assertEquals("Top level element is not completed", info.getDescription());
1394 public static ProperTextRange makeEditorWindowVisible(Point viewPosition, Editor editor) {
1395 ((EditorImpl)editor).getScrollPane().getViewport().setSize(1000, 1000);
1396 DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true);
1398 editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
1399 ((EditorImpl)editor).getScrollPane().getViewport().setViewPosition(viewPosition);
1400 ((EditorImpl)editor).getScrollPane().getViewport().setExtentSize(new Dimension(100, ((EditorImpl)editor).getPreferredHeight() -
1402 return VisibleHighlightingPassFactory.calculateVisibleRange(editor);
1406 public void testEnterInCodeBlock() throws Throwable {
1407 String text = "class LQF {\n" +
1408 " int wwwwwwwwwwww;\n" +
1409 " public void main() {<caret>\n" +
1410 " wwwwwwwwwwww = 1;\n" +
1413 configureByText(StdFileTypes.JAVA, text);
1415 ((EditorImpl)myEditor).getScrollPane().getViewport().setSize(1000, 1000);
1417 List<HighlightInfo> infos = doHighlighting(HighlightInfoType.SYMBOL_TYPE_SEVERITY);
1418 assertEquals(4, infos.size());
1421 infos = doHighlighting(HighlightInfoType.SYMBOL_TYPE_SEVERITY);
1422 assertEquals(4, infos.size());
1426 infos = doHighlighting(HighlightInfoType.SYMBOL_TYPE_SEVERITY);
1427 assertEquals(4, infos.size());
1431 public void testTypingNearEmptyErrorElement() throws Throwable {
1432 String text = "class LQF {\n" +
1433 " public void main() {\n" +
1434 " int wwwwwwwwwwww = 1<caret>\n" +
1437 configureByText(StdFileTypes.JAVA, text);
1439 ((EditorImpl)myEditor).getScrollPane().getViewport().setSize(1000, 1000);
1441 List<HighlightInfo> infos = highlightErrors();
1442 assertEquals(1, infos.size());
1445 infos = highlightErrors();
1450 public void testLIPGetAllParentsAfterCodeBlockModification() throws Throwable {
1452 String text = "class LQF {\n" +
1454 " public void me() {\n" +
1458 configureByText(StdFileTypes.JAVA, text);
1460 final List<PsiElement> visitedElements = Collections.synchronizedList(new ArrayList<>());
1461 class MyVisitor extends PsiElementVisitor {
1463 public void visitElement(PsiElement element) {
1464 visitedElements.add(element);
1467 final LocalInspectionTool tool = new LocalInspectionTool() {
1471 public String getGroupDisplayName() {
1478 public String getDisplayName() {
1479 return getGroupDisplayName();
1484 public String getShortName() {
1485 return getGroupDisplayName();
1490 public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
1491 return new MyVisitor();
1494 disposeOnTearDown(() -> disableInspectionTool(tool.getShortName()));
1495 enableInspectionTool(tool);
1497 List<HighlightInfo> infos = highlightErrors();
1500 List<PsiElement> allPsi = CollectHighlightsUtil.getElementsInRange(myFile, 0, myFile.getTextLength());
1501 assertEquals(new HashSet<>(allPsi), new HashSet<>(visitedElements));
1503 // inside code block modification
1504 visitedElements.clear();
1508 infos = highlightErrors();
1511 PsiMethod method = ((PsiJavaFile)myFile).getClasses()[0].getMethods()[0];
1512 List<PsiElement> methodAndParents =
1513 CollectHighlightsUtil.getElementsInRange(myFile, method.getTextRange().getStartOffset(), method.getTextRange().getEndOffset(), true);
1514 assertEquals(new HashSet<>(methodAndParents), new HashSet<>(visitedElements));
1518 public void testCancelsItSelfOnTypingInAlienProject() throws Throwable {
1519 String body = StringUtil.repeat("\"String field = null;\"\n", 1000);
1520 configureByText(StdFileTypes.JAVA, "class X{ void f() {" + body + "<caret>\n} }");
1522 File temp = createTempDirectory();
1523 final Project alienProject = createProject(temp + "/alien.ipr", DebugUtil.currentStackTrace());
1524 boolean succ2 = ProjectManagerEx.getInstanceEx().openProject(alienProject);
1526 DaemonProgressIndicator.setDebug(true);
1527 final DaemonProgressIndicator[] indicator = new DaemonProgressIndicator[1];
1530 Module alienModule = doCreateRealModuleIn("x", alienProject, getModuleType());
1531 final VirtualFile alienRoot = PsiTestUtil.createTestProjectStructure(alienProject, alienModule, myFilesToDelete);
1532 OpenFileDescriptor alienDescriptor = new WriteAction<OpenFileDescriptor>() {
1534 protected void run(@NotNull Result<OpenFileDescriptor> result) throws Throwable {
1535 VirtualFile alienFile = alienRoot.createChildData(this, "X.java");
1536 setFileText(alienFile, "class Alien { }");
1537 OpenFileDescriptor alienDescriptor = new OpenFileDescriptor(alienProject, alienFile);
1538 result.setResult(alienDescriptor);
1540 }.execute().throwException().getResultObject();
1542 FileEditorManager fe = FileEditorManager.getInstance(alienProject);
1543 final Editor alienEditor = fe.openTextEditor(alienDescriptor, false);
1544 ((EditorImpl)alienEditor).setCaretActive();
1545 PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
1546 PsiDocumentManager.getInstance(alienProject).commitAllDocuments();
1548 // start daemon in main project. should check for its cancel when typing in alien
1549 TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(getEditor());
1550 DaemonCodeAnalyzerImpl di = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
1551 final boolean[] checked = {false};
1552 di.runPasses(getFile(), getEditor().getDocument(), textEditor, ArrayUtil.EMPTY_INT_ARRAY, true, () -> {
1553 if (checked[0]) return;
1555 indicator[0] = myDaemonCodeAnalyzer.getUpdateProgress();
1556 typeInAlienEditor(alienEditor, 'x');
1559 catch (ProcessCanceledException ignored) {
1560 //DaemonProgressIndicator.setDebug(true);
1561 //System.out.println("indicator = " + indicator[0]);
1565 ProjectManagerEx.getInstanceEx().closeAndDispose(alienProject);
1567 fail("must throw PCE");
1570 public void testPasteInAnonymousCodeBlock() throws Throwable {
1571 configureByText(StdFileTypes.JAVA, "class X{ void f() {" +
1573 " Runnable r = new Runnable() { public void run() {\n" +
1576 " <selection>int y = x;</selection>\n " +
1578 assertEmpty(highlightErrors());
1580 assertEquals("int y = x;", getEditor().getSelectionModel().getSelectedText());
1581 getEditor().getSelectionModel().removeSelection();
1583 List<HighlightInfo> errors = highlightErrors();
1584 assertEquals(1, errors.size());
1587 private void paste() {
1588 EditorActionManager actionManager = EditorActionManager.getInstance();
1589 final EditorActionHandler actionHandler = actionManager.getActionHandler(IdeActions.ACTION_EDITOR_PASTE);
1590 WriteCommandAction.runWriteCommandAction(null, () -> actionHandler.execute(getEditor(), null, DataManager.getInstance().getDataContext()));
1593 private void copy() {
1594 EditorActionManager actionManager = EditorActionManager.getInstance();
1595 final EditorActionHandler actionHandler = actionManager.getActionHandler(IdeActions.ACTION_EDITOR_COPY);
1596 WriteCommandAction.runWriteCommandAction(null, () -> actionHandler.execute(getEditor(), null, DataManager.getInstance().getDataContext()));
1599 public void testReactivityPerformance() throws Throwable {
1600 @NonNls String filePath = "/psi/resolve/Thinlet.java";
1601 configureByFile(filePath);
1603 CompletionContributor.forLanguage(getFile().getLanguage());
1606 final DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
1607 int N = Math.max(5, Timings.adjustAccordingToMySpeed(80, true));
1608 System.out.println("N = " + N);
1609 final long[] interruptTimes = new long[N];
1610 for (int i = 0; i < N; i++) {
1611 codeAnalyzer.restart();
1612 final int finalI = i;
1613 final long start = System.currentTimeMillis();
1614 final AtomicLong typingStart = new AtomicLong();
1615 final AtomicReference<RuntimeException> exception = new AtomicReference<>();
1616 Thread watcher = new Thread("reactivity watcher") {
1620 final long start1 = typingStart.get();
1621 if (start1 == -1) break;
1626 catch (InterruptedException e1) {
1627 throw new RuntimeException(e1);
1631 long elapsed = System.currentTimeMillis() - start1;
1632 if (elapsed > 500) {
1633 // too long, see WTF
1634 String message = "Too long interrupt: " + elapsed +
1635 "; Progress: " + codeAnalyzer.getUpdateProgress() +
1636 "\n----------------------------";
1637 dumpThreadsToConsole();
1638 exception.set(new RuntimeException(message));
1639 throw exception.get();
1645 PsiFile file = getFile();
1646 Editor editor = getEditor();
1647 Project project = file.getProject();
1648 CodeInsightTestFixtureImpl.ensureIndexesUpToDate(project);
1649 TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(editor);
1650 PsiDocumentManager.getInstance(myProject).commitAllDocuments();
1652 Runnable interrupt = () -> {
1653 long now = System.currentTimeMillis();
1654 if (now - start < 100) {
1655 // wait to engage all highlighting threads
1658 typingStart.set(System.currentTimeMillis());
1660 long end = System.currentTimeMillis();
1661 long interruptTime = end - now;
1662 interruptTimes[finalI] = interruptTime;
1663 assertNull(codeAnalyzer.getUpdateProgress());
1664 System.out.println(interruptTime);
1665 throw new ProcessCanceledException();
1667 long hiStart = System.currentTimeMillis();
1668 codeAnalyzer.runPasses(file, editor.getDocument(), textEditor, ArrayUtil.EMPTY_INT_ARRAY, false, interrupt);
1669 long hiEnd = System.currentTimeMillis();
1670 DaemonProgressIndicator progress = codeAnalyzer.getUpdateProgress();
1671 String message = "Should have been interrupted: " + progress + "; Elapsed: " + (hiEnd - hiStart) + "ms";
1672 dumpThreadsToConsole();
1673 throw new RuntimeException(message);
1675 catch (ProcessCanceledException ignored) {
1678 typingStart.set(-1); // cancel watcher
1680 if (exception.get() != null) {
1681 throw exception.get();
1686 long ave = ArrayUtil.averageAmongMedians(interruptTimes, 3);
1687 System.out.println("Average among the N/3 median times: " + ave + "ms");
1688 assertTrue(ave < 300);
1691 private static void dumpThreadsToConsole() {
1692 System.err.println("----all threads---");
1693 for (Thread thread : Thread.getAllStackTraces().keySet()) {
1695 boolean canceled = CoreProgressManager.isCanceledThread(thread);
1697 System.err.println("Thread " + thread + " indicator is canceled");
1700 PerformanceWatcher.dumpThreadsToConsole("");
1701 System.err.println("----///////---");
1704 public void testTypingLatencyPerformance() throws Throwable {
1705 @NonNls String filePath = "/psi/resolve/ThinletBig.java";
1707 configureByFile(filePath);
1710 CompletionContributor.forLanguage(getFile().getLanguage());
1711 long s = System.currentTimeMillis();
1713 long e = System.currentTimeMillis();
1714 //System.out.println("Hi elapsed: "+(e-s));
1716 final DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
1717 int N = Math.max(5, Timings.adjustAccordingToMySpeed(80, true));
1718 //System.out.println("N = " + N);
1719 final long[] interruptTimes = new long[N];
1720 for (int i = 0; i < N; i++) {
1721 codeAnalyzer.restart();
1722 final int finalI = i;
1723 final long start = System.currentTimeMillis();
1724 Runnable interrupt = () -> {
1725 long now = System.currentTimeMillis();
1726 if (now - start < 100) {
1727 // wait to engage all highlighting threads
1731 long end = System.currentTimeMillis();
1732 long interruptTime = end - now;
1733 interruptTimes[finalI] = interruptTime;
1734 assertNull(codeAnalyzer.getUpdateProgress());
1735 //System.out.println(interruptTime);
1736 throw new ProcessCanceledException();
1739 PsiFile file = getFile();
1740 Editor editor = getEditor();
1741 Project project = file.getProject();
1742 CodeInsightTestFixtureImpl.ensureIndexesUpToDate(project);
1743 TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(editor);
1744 PsiDocumentManager.getInstance(myProject).commitAllDocuments();
1745 codeAnalyzer.runPasses(file, editor.getDocument(), textEditor, ArrayUtil.EMPTY_INT_ARRAY, false, interrupt);
1747 throw new RuntimeException("should have been interrupted");
1749 catch (ProcessCanceledException ignored) {
1752 //highlightErrors();
1755 long mean = ArrayUtil.averageAmongMedians(interruptTimes, 3);
1756 long avg = Arrays.stream(interruptTimes).sum() / interruptTimes.length;
1757 long max = Arrays.stream(interruptTimes).max().getAsLong();
1758 long min = Arrays.stream(interruptTimes).min().getAsLong();
1759 System.out.println("Average among the N/3 median times: " + mean + "ms; max: "+max+"; min:"+min+"; avg: "+avg);
1760 assertTrue(mean < 10);
1763 private static void startCPUProfiling() {
1765 Class<?> aClass = Class.forName("com.intellij.util.ProfilingUtil");
1766 Method method = ReflectionUtil.getDeclaredMethod(aClass, "startCPUProfiling");
1767 method.invoke(null);
1769 catch (Exception e) {
1770 throw new RuntimeException(e);
1773 private static void stopCPUProfiling() {
1775 Class<?> aClass = Class.forName("com.intellij.util.ProfilingUtil");
1776 Method method = ReflectionUtil.getDeclaredMethod(aClass, "stopCPUProfiling");
1777 method.invoke(null);
1779 catch (Exception e) {
1780 throw new RuntimeException(e);
1784 private static String captureCPUSnapshot() {
1786 Class<?> aClass = Class.forName("com.intellij.util.ProfilingUtil");
1787 Method method = ReflectionUtil.getDeclaredMethod(aClass, "captureCPUSnapshot");
1788 return (String)method.invoke(null);
1790 catch (Exception e) {
1791 throw new RuntimeException(e);
1795 public void testPostHighlightingPassRunsOnEveryPsiModification() throws Exception {
1796 PsiFile x = createFile("X.java", "public class X { public static void ffffffffffffff(){} }");
1797 PsiFile use = createFile("Use.java", "public class Use { { <caret>X.ffffffffffffff(); } }");
1798 configureByExistingFile(use.getVirtualFile());
1800 InspectionProfile profile = InspectionProjectProfileManager.getInstance(myProject).getCurrentProfile();
1801 HighlightDisplayKey myDeadCodeKey = HighlightDisplayKey.find(UnusedDeclarationInspectionBase.SHORT_NAME);
1802 if (myDeadCodeKey == null) {
1803 myDeadCodeKey = HighlightDisplayKey.register(UnusedDeclarationInspectionBase.SHORT_NAME, UnusedDeclarationInspectionBase.DISPLAY_NAME);
1805 UnusedDeclarationInspectionBase myDeadCodeInspection = new UnusedDeclarationInspectionBase(true);
1806 enableInspectionTool(myDeadCodeInspection);
1807 assert profile.isToolEnabled(myDeadCodeKey, myFile);
1809 Editor xEditor = createEditor(x.getVirtualFile());
1810 List<HighlightInfo> xInfos = filter(CodeInsightTestFixtureImpl.instantiateAndRun(x, xEditor, new int[0], false),
1811 HighlightSeverity.WARNING);
1812 HighlightInfo info = ContainerUtil.find(xInfos, xInfo -> xInfo.getDescription().equals("Method 'ffffffffffffff()' is never used"));
1813 assertNull(xInfos.toString(), info);
1815 Editor useEditor = myEditor;
1816 List<HighlightInfo> useInfos = filter(CodeInsightTestFixtureImpl.instantiateAndRun(use, useEditor, new int[0], false), HighlightSeverity.ERROR);
1817 assertEmpty(useInfos);
1822 PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
1823 xInfos = filter(CodeInsightTestFixtureImpl.instantiateAndRun(x, xEditor, new int[0], false), HighlightSeverity.WARNING);
1824 info = ContainerUtil.find(xInfos, xInfo -> xInfo.getDescription().equals("Method 'ffffffffffffff()' is never used"));
1825 assertNotNull(xInfos.toString(), info);
1829 public void testErrorDisappearsRightAfterTypingInsideVisibleAreaWhileDaemonContinuesToChugAlong() throws Throwable {
1830 String text = "class X{\nint xxx;\n{\nint i = <selection>null</selection><caret>;\n" + StringUtil.repeat("{ this.hashCode(); }\n\n\n", 10000) + "}}";
1831 configureByText(StdFileTypes.JAVA, text);
1833 ((EditorImpl)myEditor).getScrollPane().getViewport().setSize(100, 100);
1834 DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true);
1836 myEditor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
1837 ((EditorImpl)myEditor).getScrollPane().getViewport().setViewPosition(new Point(0, 0));
1838 ((EditorImpl)myEditor).getScrollPane().getViewport().setExtentSize(new Dimension(100, 100000));
1839 ProperTextRange visibleRange = VisibleHighlightingPassFactory.calculateVisibleRange(getEditor());
1840 assertTrue(visibleRange.getLength() > 0);
1841 final Document document = myEditor.getDocument();
1842 assertTrue(visibleRange.getLength() < document.getTextLength());
1844 List<HighlightInfo> err1 = highlightErrors();
1845 HighlightInfo info = assertOneElement(err1);
1846 final String errorDescription = "Incompatible types. Found: 'null', required: 'int'";
1847 assertEquals(errorDescription, info.getDescription());
1849 MarkupModelEx model = (MarkupModelEx)DocumentMarkupModel.forDocument(document, myProject, false);
1850 final boolean[] errorRemoved = {false};
1852 model.addMarkupModelListener(getTestRootDisposable(), new MarkupModelListener.Adapter() {
1854 public void beforeRemoved(@NotNull RangeHighlighterEx highlighter) {
1855 Object tt = highlighter.getErrorStripeTooltip();
1856 if (!(tt instanceof HighlightInfo)) return;
1857 String description = ((HighlightInfo)tt).getDescription();
1858 if (errorDescription.equals(description)) {
1859 errorRemoved[0] = true;
1861 List<TextEditorHighlightingPass> passes = myDaemonCodeAnalyzer.getPassesToShowProgressFor(document);
1862 GeneralHighlightingPass ghp = null;
1863 for (TextEditorHighlightingPass pass : passes) {
1864 if (pass instanceof GeneralHighlightingPass && pass.getId() == Pass.UPDATE_ALL) {
1865 assert ghp == null : ghp;
1866 ghp = (GeneralHighlightingPass)pass;
1870 boolean finished = ghp.isFinished();
1871 assertFalse(finished);
1877 List<HighlightInfo> errors = highlightErrors();
1878 assertEmpty(errors);
1879 assertTrue(errorRemoved[0]);
1882 public void testDaemonWorksForDefaultProjectSinceItIsNeededInSettingsDialogForSomeReason() {
1883 assertNotNull(DaemonCodeAnalyzer.getInstance(ProjectManager.getInstance().getDefaultProject()));
1886 public void testChangeEventsAreNotAlwaysGeneric() throws Exception {
1887 String body = "class X {\n" +
1888 "<caret> @org.PPP\n" +
1889 " void dtoArrayDouble() {\n" +
1892 configureByText(JavaFileType.INSTANCE, body);
1893 makeEditorWindowVisible(new Point(), myEditor);
1895 List<HighlightInfo> errors = highlightErrors();
1896 assertFalse(errors.isEmpty());
1899 errors = highlightErrors();
1900 assertEmpty(errors);
1904 errors = highlightErrors();
1905 assertFalse(errors.isEmpty());
1908 public void testInterruptOnTyping() throws Throwable {
1909 @NonNls String filePath = "/psi/resolve/Thinlet.java";
1910 configureByFile(filePath);
1913 final DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
1914 codeAnalyzer.restart();
1915 Runnable interrupt = () -> type(' ');
1917 PsiDocumentManager.getInstance(myProject).commitAllDocuments();
1919 PsiFile file = getFile();
1920 Editor editor = getEditor();
1921 Project project = file.getProject();
1922 CodeInsightTestFixtureImpl.ensureIndexesUpToDate(project);
1923 TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(editor);
1924 codeAnalyzer.runPasses(file, editor.getDocument(), textEditor, ArrayUtil.EMPTY_INT_ARRAY, true, interrupt);
1926 catch (ProcessCanceledException ignored) {
1929 fail("PCE must have been thrown");
1932 public void testAllPassesFinishAfterInterruptOnTyping_Performance() throws Throwable {
1933 @NonNls String filePath = "/psi/resolve/Thinlet.java";
1934 configureByFile(filePath);
1937 final DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
1938 Runnable interrupt = () -> type(' ');
1940 for (int i=0; i<100; i++) {
1942 codeAnalyzer.restart();
1944 PsiDocumentManager.getInstance(myProject).commitAllDocuments();
1946 PsiFile file = getFile();
1947 Editor editor = getEditor();
1948 Project project = file.getProject();
1949 CodeInsightTestFixtureImpl.ensureIndexesUpToDate(project);
1950 TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(editor);
1951 codeAnalyzer.runPasses(file, editor.getDocument(), textEditor, ArrayUtil.EMPTY_INT_ARRAY, true, interrupt);
1953 catch (ProcessCanceledException ignored) {
1954 codeAnalyzer.waitForTermination();
1957 fail("PCE must have been thrown");
1961 public void testCodeFoldingInSplittedWindowAppliesToAllEditors() throws Exception {
1962 final Set<Editor> applied = new THashSet<>();
1963 final Set<Editor> collected = new THashSet<>();
1964 registerFakePass(applied, collected);
1966 configureByText(PlainTextFileType.INSTANCE, "");
1967 Editor editor1 = getEditor();
1968 final Editor editor2 = EditorFactory.getInstance().createEditor(editor1.getDocument(),getProject());
1969 Disposer.register(getProject(), () -> EditorFactory.getInstance().releaseEditor(editor2));
1970 TextEditor textEditor1 = new PsiAwareTextEditorProvider().getTextEditor(editor1);
1971 TextEditor textEditor2 = new PsiAwareTextEditorProvider().getTextEditor(editor2);
1973 List<HighlightInfo> errors = myDaemonCodeAnalyzer.runPasses(myFile, editor1.getDocument(), Arrays.asList(textEditor1,textEditor2), new int[0], false, null);
1974 assertEmpty(errors);
1976 assertEquals(collected, ContainerUtil.newHashSet(editor1, editor2));
1977 assertEquals(applied, ContainerUtil.newHashSet(editor1, editor2));
1980 private void registerFakePass(@NotNull final Set<Editor> applied, @NotNull final Set<Editor> collected) {
1981 class Fac extends AbstractProjectComponent implements TextEditorHighlightingPassFactory {
1982 private Fac(Project project) {
1987 public TextEditorHighlightingPass createHighlightingPass(@NotNull PsiFile file, @NotNull final Editor editor) {
1988 return new EditorBoundHighlightingPass(editor, file, false) {
1990 public void doCollectInformation(@NotNull ProgressIndicator progress) {
1991 collected.add(editor);
1995 public void doApplyInformationToEditor() {
1996 applied.add(editor);
2001 TextEditorHighlightingPassRegistrar registrar = TextEditorHighlightingPassRegistrar.getInstance(getProject());
2002 registrar.registerTextEditorHighlightingPass(new Fac(getProject()), null, null, false, -1);
2005 private volatile boolean runHeavyProcessing;
2006 public void testDaemonDisablesItselfDuringHeavyProcessing() throws Exception {
2007 runHeavyProcessing = false;
2008 DaemonCodeAnalyzerSettings settings = DaemonCodeAnalyzerSettings.getInstance();
2009 int delay = settings.AUTOREPARSE_DELAY;
2010 settings.AUTOREPARSE_DELAY = 0;
2012 final Set<Editor> applied = Collections.synchronizedSet(new THashSet<>());
2013 final Set<Editor> collected = Collections.synchronizedSet(new THashSet<>());
2014 registerFakePass(applied, collected);
2016 configureByText(PlainTextFileType.INSTANCE, "");
2017 Editor editor = getEditor();
2018 EditorTracker editorTracker = getProject().getComponent(EditorTracker.class);
2019 editorTracker.setActiveEditors(Collections.singletonList(editor));
2020 while (HeavyProcessLatch.INSTANCE.isRunning()) {
2021 UIUtil.dispatchAllInvocationEvents();
2023 type("xxx"); // restart daemon
2024 assertTrue(editorTracker.getActiveEditors().contains(editor));
2025 assertSame(editor, FileEditorManager.getInstance(myProject).getSelectedTextEditor());
2028 // wait for first pass to complete
2029 long start = System.currentTimeMillis();
2030 while (myDaemonCodeAnalyzer.isRunning() || !applied.contains(editor)) {
2031 UIUtil.dispatchAllInvocationEvents();
2032 if (System.currentTimeMillis() - start > 10000) {
2033 fail("Too long waiting for daemon");
2037 runHeavyProcessing = true;
2038 ApplicationManager.getApplication().executeOnPooledThread(() -> {
2039 AccessToken token = HeavyProcessLatch.INSTANCE.processStarted("my own heavy op");
2041 while (runHeavyProcessing) {
2048 while (!HeavyProcessLatch.INSTANCE.isRunning()) {
2049 UIUtil.dispatchAllInvocationEvents();
2054 type("xxx"); // try to restart daemon
2056 start = System.currentTimeMillis();
2057 while (System.currentTimeMillis() < start + 5000) {
2058 assertEmpty(applied); // it should not restart
2059 assertEmpty(collected);
2060 UIUtil.dispatchAllInvocationEvents();
2064 runHeavyProcessing = false;
2065 settings.AUTOREPARSE_DELAY = delay;
2070 public void testModificationInsideCodeBlockDoesNotRehighlightWholeFile() throws Exception {
2071 configureByText(JavaFileType.INSTANCE, "class X { int f = \"error\"; int f() { int gg<caret> = 11; return 0;} }");
2072 List<HighlightInfo> errors = highlightErrors();
2073 assertEquals(1, errors.size());
2074 assertEquals("Incompatible types. Found: 'java.lang.String', required: 'int'", errors.get(0).getDescription());
2076 errors.get(0).highlighter.dispose();
2078 errors = highlightErrors();
2079 assertEmpty(errors);
2082 errors = highlightErrors();
2083 assertEmpty(errors);
2085 myEditor.getCaretModel().moveToOffset(0);
2087 errors = highlightErrors();
2088 assertEquals(1, errors.size());
2089 assertEquals("Incompatible types. Found: 'java.lang.String', required: 'int'", errors.get(0).getDescription());
2092 public void _testCaretMovementDoesNotRestartHighlighting() throws Exception {
2093 configureByText(JavaFileType.INSTANCE, "class X { int f = \"error\"; int f() { int gg<caret> = 11; return 0;} }");
2095 TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(getEditor());
2096 final DaemonCodeAnalyzerImpl di = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
2097 final AtomicReference<ProgressIndicator> indicator = new AtomicReference<>();
2098 final List<HighlightInfo> errors = filter(
2099 di.runPasses(getFile(), getEditor().getDocument(), textEditor, ArrayUtil.EMPTY_INT_ARRAY, false, () -> {
2100 if (indicator.get() == null) {
2101 indicator.set(di.getUpdateProgress());
2103 assertSame(indicator.get(), di.getUpdateProgress());
2105 if (getEditor().getCaretModel().getOffset() == getEditor().getDocument().getTextLength()-1) {
2106 getEditor().getCaretModel().moveToOffset(0);
2108 }), HighlightSeverity.ERROR);
2110 assertEquals(1, errors.size());
2111 assertEquals("Incompatible types. Found: 'java.lang.String', required: 'int'", errors.get(0).getDescription());
2115 public void testHighlightingDoesWaitForEmbarrassinglySlowExternalAnnotatorsToFinish() throws Exception {
2116 configureByText(JavaFileType.INSTANCE, "class X { int f() { int gg<caret> = 11; return 0;} }");
2117 final AtomicBoolean run = new AtomicBoolean();
2118 final int SLEEP = 20000;
2119 ExternalAnnotator<Integer, Integer> annotator = new ExternalAnnotator<Integer, Integer>() {
2122 public Integer collectInformation(@NotNull PsiFile file) {
2128 public Integer doAnnotate(final Integer collectedInfo) {
2129 TimeoutUtil.sleep(SLEEP);
2134 public void apply(@NotNull final PsiFile file, final Integer annotationResult, @NotNull final AnnotationHolder holder) {
2138 ExternalLanguageAnnotators.INSTANCE.addExplicitExtension(JavaLanguage.INSTANCE, annotator);
2141 long start = System.currentTimeMillis();
2142 List<HighlightInfo> errors = filter(CodeInsightTestFixtureImpl.instantiateAndRun(getFile(), getEditor(), new int[0], false),
2143 HighlightSeverity.ERROR);
2144 long elapsed = System.currentTimeMillis() - start;
2146 assertEquals(0, errors.size());
2148 fail(ThreadDumper.dumpThreadsToString());
2150 assertTrue("Elapsed: "+elapsed, elapsed >= SLEEP);
2153 ExternalLanguageAnnotators.INSTANCE.removeExplicitExtension(JavaLanguage.INSTANCE, annotator);
2157 public void testModificationInExcludedFileDoesNotCauseRehighlight() throws Exception {
2158 final PsiFile excluded = configureByText(JavaFileType.INSTANCE, "class EEE { void f(){} }");
2159 PsiTestUtil.addExcludedRoot(myModule, excluded.getVirtualFile().getParent());
2161 configureByText(JavaFileType.INSTANCE, "class X { <caret> }");
2162 List<HighlightInfo> errors = highlightErrors();
2163 assertEmpty(errors);
2164 FileStatusMap me = DaemonCodeAnalyzerEx.getInstanceEx(getProject()).getFileStatusMap();
2165 TextRange scope = me.getFileDirtyScope(getEditor().getDocument(), Pass.UPDATE_ALL);
2168 WriteCommandAction.runWriteCommandAction(getProject(), () -> ((PsiJavaFile)excluded).getClasses()[0].getMethods()[0].delete());
2170 UIUtil.dispatchAllInvocationEvents();
2171 scope = me.getFileDirtyScope(getEditor().getDocument(), Pass.UPDATE_ALL);
2175 public void testModificationInWorkspaceXmlDoesNotCauseRehighlight() throws Exception {
2176 configureByText(JavaFileType.INSTANCE, "class X { <caret> }");
2177 ApplicationEx application = ApplicationManagerEx.getApplicationEx();
2178 boolean appSave = application.isDoNotSave();
2179 application.doNotSave(false);
2181 application.saveAll();
2182 final PsiFile excluded = PsiManager.getInstance(getProject()).findFile(getProject().getWorkspaceFile());
2184 List<HighlightInfo> errors = highlightErrors();
2185 assertEmpty(errors);
2186 FileStatusMap me = DaemonCodeAnalyzerEx.getInstanceEx(getProject()).getFileStatusMap();
2187 TextRange scope = me.getFileDirtyScope(getEditor().getDocument(), Pass.UPDATE_ALL);
2190 WriteCommandAction.runWriteCommandAction(getProject(), () -> {
2191 Document document = PsiDocumentManager.getInstance(getProject()).getDocument(excluded);
2192 document.insertString(0, "<!-- dsfsd -->");
2193 PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
2195 UIUtil.dispatchAllInvocationEvents();
2196 scope = me.getFileDirtyScope(getEditor().getDocument(), Pass.UPDATE_ALL);
2200 application.doNotSave(appSave);
2204 public void testLightBulbDoesNotUpdateIntentionsInEDT() throws Exception {
2205 final IntentionAction longLongUpdate = new AbstractIntentionAction() {
2207 public void invoke(@NotNull Project project, Editor editor, PsiFile file) {
2213 public String getText() {
2214 return "LongAction";
2218 public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
2219 if (ApplicationManager.getApplication().isDispatchThread()) {
2220 throw new RuntimeException("Must not update actions in EDT");
2225 IntentionManager.getInstance().addAction(longLongUpdate);
2226 Disposer.register(getTestRootDisposable(), () -> IntentionManager.getInstance().unregisterIntention(longLongUpdate));
2227 configureByText(JavaFileType.INSTANCE, "class X { <caret> }");
2228 makeEditorWindowVisible(new Point(0, 0), myEditor);
2230 myDaemonCodeAnalyzer.restart();
2231 DaemonCodeAnalyzerSettings mySettings = DaemonCodeAnalyzerSettings.getInstance();
2232 mySettings.AUTOREPARSE_DELAY = 0;
2233 for (int i=0; i<1000; i++) {
2235 UIUtil.dispatchAllInvocationEvents();
2237 DaemonProgressIndicator updateProgress = myDaemonCodeAnalyzer.getUpdateProgress();
2238 while(myDaemonCodeAnalyzer.getUpdateProgress() == updateProgress) { // wait until daemon started
2239 UIUtil.dispatchAllInvocationEvents();
2241 long start = System.currentTimeMillis();
2242 while (myDaemonCodeAnalyzer.isRunning() && System.currentTimeMillis() < start + 500) {
2243 UIUtil.dispatchAllInvocationEvents(); // wait for a bit more until ShowIntentionsPass.doApplyInformationToEditor() called
2248 public void testLightBulbIsHiddenWhenFixRangeIsCollapsed() {
2249 configureByText(StdFileTypes.JAVA, "class S { void foo() { boolean var; if (<selection>va<caret>r</selection>) {}} }");
2250 ((EditorImpl)myEditor).getScrollPane().getViewport().setSize(1000, 1000);
2252 final Set<LightweightHint> visibleHints = ContainerUtil.newIdentityTroveSet();
2253 getProject().getMessageBus().connect(getTestRootDisposable()).subscribe(EditorHintListener.TOPIC, new EditorHintListener() {
2255 public void hintShown(final Project project, final LightweightHint hint, final int flags) {
2256 visibleHints.add(hint);
2257 hint.addHintListener(new HintListener() {
2259 public void hintHidden(EventObject event) {
2260 visibleHints.remove(hint);
2261 hint.removeHintListener(this);
2268 IntentionHintComponent lastHintBeforeDeletion = myDaemonCodeAnalyzer.getLastIntentionHint();
2269 assertNotNull(lastHintBeforeDeletion);
2273 IntentionHintComponent lastHintAfterDeletion = myDaemonCodeAnalyzer.getLastIntentionHint();
2274 assertSame(lastHintBeforeDeletion, lastHintAfterDeletion);
2276 assertEmpty(visibleHints);
2279 public void testCodeFoldingPassRestartsOnRegionUnfolding() throws Exception {
2280 DaemonCodeAnalyzerSettings settings = DaemonCodeAnalyzerSettings.getInstance();
2281 int savedDelay = settings.AUTOREPARSE_DELAY;
2282 settings.AUTOREPARSE_DELAY = 0;
2284 configureByText(StdFileTypes.JAVA, "class Foo {\n" +
2289 CodeFoldingManager.getInstance(getProject()).buildInitialFoldings(myEditor);
2291 EditorTestUtil.executeAction(myEditor, IdeActions.ACTION_COLLAPSE_ALL_REGIONS);
2293 checkFoldingState("[FoldRegion +(25:33), placeholder='{...}']");
2295 new WriteCommandAction<Void>(myProject){
2297 protected void run(@NotNull Result<Void> result) throws Throwable {
2298 myEditor.getDocument().insertString(0, "/*");
2302 checkFoldingState("[FoldRegion -(0:37), placeholder='/.../', FoldRegion +(27:35), placeholder='{...}']");
2304 EditorTestUtil.executeAction(myEditor, IdeActions.ACTION_EXPAND_ALL_REGIONS);
2306 checkFoldingState("[FoldRegion -(0:37), placeholder='/.../']");
2309 settings.AUTOREPARSE_DELAY = savedDelay;
2313 private void checkFoldingState(String expected) {
2314 assertEquals(expected, Arrays.toString(myEditor.getFoldingModel().getAllFoldRegions()));
2317 private void waitForDaemon() {
2318 long deadline = System.currentTimeMillis() + 60_000;
2319 while (!daemonIsWorkingOrPending()) {
2320 if (System.currentTimeMillis() > deadline) fail("Too long waiting for daemon to start");
2321 UIUtil.dispatchInvocationEvent();
2323 while (daemonIsWorkingOrPending()) {
2324 if (System.currentTimeMillis() > deadline) fail("Too long waiting for daemon to finish");
2325 UIUtil.dispatchInvocationEvent();
2329 private boolean daemonIsWorkingOrPending() {
2330 return PsiDocumentManager.getInstance(myProject).isUncommited(myEditor.getDocument()) || myDaemonCodeAnalyzer.isRunningOrPending();
2333 public void testRehighlightInDebuggerExpressionFragment() throws Exception {
2334 PsiExpressionCodeFragment fragment = JavaCodeFragmentFactory.getInstance(getProject()).createExpressionCodeFragment("+ <caret>\"a\"", null,
2335 PsiType.getJavaLangObject(getPsiManager(), GlobalSearchScope.allScope(getProject())), true);
2337 Document document = PsiDocumentManager.getInstance(getProject()).getDocument(fragment);
2338 myEditor = EditorFactory.getInstance().createEditor(document, getProject(), StdFileTypes.JAVA, false);
2340 ProperTextRange visibleRange = makeEditorWindowVisible(new Point(0, 0), myEditor);
2341 assertEquals(document.getTextLength(), visibleRange.getLength());
2344 final EditorInfo editorInfo = new EditorInfo(document.getText());
2346 final String newFileText = editorInfo.getNewFileText();
2347 ApplicationManager.getApplication().runWriteAction(() -> {
2348 if (!document.getText().equals(newFileText)) {
2349 document.setText(newFileText);
2352 editorInfo.applyToEditor(myEditor);
2355 PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
2358 List<HighlightInfo> errors = highlightErrors();
2359 HighlightInfo error = assertOneElement(errors);
2360 assertEquals("Operator '+' cannot be applied to 'java.lang.String'", error.getDescription());
2364 Collection<HighlightInfo> afterTyping = highlightErrors();
2365 HighlightInfo after = assertOneElement(afterTyping);
2366 assertEquals("Operator '+' cannot be applied to 'java.lang.String'", after.getDescription());
2369 EditorFactory.getInstance().releaseEditor(myEditor);
2373 public void testFileReload() throws Exception {
2374 VirtualFile file = createFile("a.java", "").getVirtualFile();
2375 Document document = getDocument(file);
2376 assertNotNull(document);
2378 FileStatusMap fileStatusMap = myDaemonCodeAnalyzer.getFileStatusMap();
2380 WriteCommandAction.runWriteCommandAction(getProject(), () -> {
2381 PlatformTestUtil.tryGcSoftlyReachableObjects();
2382 assertNull(PsiDocumentManager.getInstance(getProject()).getCachedPsiFile(document));
2384 document.insertString(0, "class X { void foo() {}}");
2385 assertEquals(TextRange.from(0, document.getTextLength()), fileStatusMap.getFileDirtyScope(document, Pass.UPDATE_ALL));
2387 FileContentUtilCore.reparseFiles(file);
2388 assertEquals(TextRange.from(0, document.getTextLength()), fileStatusMap.getFileDirtyScope(document, Pass.UPDATE_ALL));
2390 findClass("X").getMethods()[0].delete();
2391 assertEquals(TextRange.from(0, document.getTextLength()), fileStatusMap.getFileDirtyScope(document, Pass.UPDATE_ALL));