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.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.AnActionEvent;
62 import com.intellij.openapi.actionSystem.DataConstants;
63 import com.intellij.openapi.actionSystem.DataContext;
64 import com.intellij.openapi.actionSystem.IdeActions;
65 import com.intellij.openapi.actionSystem.impl.SimpleDataContext;
66 import com.intellij.openapi.application.AccessToken;
67 import com.intellij.openapi.application.ApplicationManager;
68 import com.intellij.openapi.application.Result;
69 import com.intellij.openapi.application.WriteAction;
70 import com.intellij.openapi.application.ex.ApplicationEx;
71 import com.intellij.openapi.application.ex.ApplicationManagerEx;
72 import com.intellij.openapi.command.CommandProcessor;
73 import com.intellij.openapi.command.WriteCommandAction;
74 import com.intellij.openapi.command.undo.UndoManager;
75 import com.intellij.openapi.components.AbstractProjectComponent;
76 import com.intellij.openapi.components.impl.stores.StorageUtil;
77 import com.intellij.openapi.editor.*;
78 import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
79 import com.intellij.openapi.editor.actionSystem.EditorActionManager;
80 import com.intellij.openapi.editor.actionSystem.TypedAction;
81 import com.intellij.openapi.editor.ex.EditorEx;
82 import com.intellij.openapi.editor.ex.MarkupModelEx;
83 import com.intellij.openapi.editor.ex.RangeHighlighterEx;
84 import com.intellij.openapi.editor.impl.DocumentMarkupModel;
85 import com.intellij.openapi.editor.impl.EditorImpl;
86 import com.intellij.openapi.editor.impl.event.MarkupModelListener;
87 import com.intellij.openapi.editor.markup.GutterIconRenderer;
88 import com.intellij.openapi.editor.markup.HighlighterTargetArea;
89 import com.intellij.openapi.editor.markup.MarkupModel;
90 import com.intellij.openapi.editor.markup.RangeHighlighter;
91 import com.intellij.openapi.fileEditor.FileEditorManager;
92 import com.intellij.openapi.fileEditor.OpenFileDescriptor;
93 import com.intellij.openapi.fileEditor.TextEditor;
94 import com.intellij.openapi.fileEditor.impl.text.PsiAwareTextEditorProvider;
95 import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider;
96 import com.intellij.openapi.fileTypes.PlainTextFileType;
97 import com.intellij.openapi.fileTypes.StdFileTypes;
98 import com.intellij.openapi.module.Module;
99 import com.intellij.openapi.progress.ProcessCanceledException;
100 import com.intellij.openapi.progress.ProgressIndicator;
101 import com.intellij.openapi.progress.impl.CoreProgressManager;
102 import com.intellij.openapi.project.Project;
103 import com.intellij.openapi.project.ProjectManager;
104 import com.intellij.openapi.project.ex.ProjectManagerEx;
105 import com.intellij.openapi.project.impl.ProjectManagerImpl;
106 import com.intellij.openapi.util.Disposer;
107 import com.intellij.openapi.util.ProperTextRange;
108 import com.intellij.openapi.util.Segment;
109 import com.intellij.openapi.util.TextRange;
110 import com.intellij.openapi.util.text.StringUtil;
111 import com.intellij.openapi.vfs.VirtualFile;
112 import com.intellij.profile.codeInspection.InspectionProfileManager;
113 import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
114 import com.intellij.psi.*;
115 import com.intellij.psi.impl.DebugUtil;
116 import com.intellij.psi.search.GlobalSearchScope;
117 import com.intellij.refactoring.inline.InlineRefactoringActionHandler;
118 import com.intellij.refactoring.rename.RenameProcessor;
119 import com.intellij.testFramework.*;
120 import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl;
121 import com.intellij.ui.HintListener;
122 import com.intellij.ui.LightweightHint;
123 import com.intellij.util.*;
124 import com.intellij.util.containers.ContainerUtil;
125 import com.intellij.util.io.storage.HeavyProcessLatch;
126 import com.intellij.util.ui.UIUtil;
127 import com.intellij.xml.util.CheckDtdReferencesInspection;
128 import gnu.trove.THashSet;
129 import org.jetbrains.annotations.Nls;
130 import org.jetbrains.annotations.NonNls;
131 import org.jetbrains.annotations.NotNull;
132 import org.jetbrains.annotations.Nullable;
136 import java.io.IOException;
137 import java.lang.reflect.Method;
139 import java.util.List;
140 import java.util.concurrent.atomic.AtomicBoolean;
141 import java.util.concurrent.atomic.AtomicLong;
142 import java.util.concurrent.atomic.AtomicReference;
148 public class DaemonRespondToChangesTest extends DaemonAnalyzerTestCase {
149 private static final String BASE_PATH = "/codeInsight/daemonCodeAnalyzer/typing/";
151 private DaemonCodeAnalyzerImpl myDaemonCodeAnalyzer;
154 protected void setUp() throws Exception {
156 enableInspectionTool(new UnusedDeclarationInspection());
157 myDaemonCodeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
158 UndoManager.getInstance(myProject);
159 myDaemonCodeAnalyzer.setUpdateByTimerEnabled(true);
160 DaemonProgressIndicator.setDebug(true);
164 protected void tearDown() throws Exception {
166 Project project = getProject();
167 if (project != null) {
168 doPostponedFormatting(project);
169 ((ProjectManagerImpl)ProjectManagerEx.getInstanceEx()).closeProject(project, false, false, false);
178 protected boolean doTestLineMarkers() {
183 protected void setUpProject() throws Exception {
184 super.setUpProject();
185 ProjectManagerEx.getInstanceEx().openProject(getProject());
186 super.runStartupActivities();
187 UIUtil.dispatchAllInvocationEvents(); // startup activities
191 protected void runStartupActivities() {
195 private static void typeInAlienEditor(Editor alienEditor, char c) {
196 TypedAction action = EditorActionManager.getInstance().getTypedAction();
197 DataContext dataContext = ((EditorEx)alienEditor).getDataContext();
199 action.actionPerformed(alienEditor, c, dataContext);
203 public void testHighlightersUpdate() throws Exception {
204 configureByFile(BASE_PATH + "HighlightersUpdate.java");
205 Document document = getDocument(getFile());
207 List<HighlightInfo> errors = DaemonCodeAnalyzerImpl.getHighlights(document, HighlightSeverity.ERROR, getProject());
208 assertEquals(1, errors.size());
209 TextRange dirty = myDaemonCodeAnalyzer.getFileStatusMap().getFileDirtyScope(document, Pass.UPDATE_ALL);
213 PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
214 dirty = myDaemonCodeAnalyzer.getFileStatusMap().getFileDirtyScope(document, Pass.UPDATE_ALL);
215 assertNotNull(dirty);
219 public void testNoPsiEventsAltogether() throws Exception {
220 configureByFile(BASE_PATH + "HighlightersUpdate.java");
221 Document document = getDocument(getFile());
225 PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
227 TextRange dirty = myDaemonCodeAnalyzer.getFileStatusMap().getFileDirtyScope(document, Pass.UPDATE_ALL);
228 assertEquals(getFile().getTextRange(), dirty); // have to rehighlight whole file in case no PSI events have come
231 public void testRenameClass() throws Exception {
232 configureByFile(BASE_PATH + "AClass.java");
233 Document document = getDocument(getFile());
234 Collection<HighlightInfo> infos = highlightErrors();
235 assertEquals(0, infos.size());
236 final PsiClass psiClass = ((PsiJavaFile)getFile()).getClasses()[0];
237 ApplicationManager.getApplication().runWriteAction(() -> {
238 new RenameProcessor(myProject, psiClass, "Class2", false, false).run();
240 TextRange dirty = myDaemonCodeAnalyzer.getFileStatusMap().getFileDirtyScope(document, Pass.UPDATE_ALL);
241 assertEquals(getFile().getTextRange(), dirty);
245 assertTrue(myDaemonCodeAnalyzer.isErrorAnalyzingFinished(getFile()));
249 public void testTypingSpace() throws Exception {
250 configureByFile(BASE_PATH + "AClass.java");
251 Document document = getDocument(getFile());
252 Collection<HighlightInfo> infos = highlightErrors();
253 assertEquals(0, infos.size());
256 PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
257 PsiElement elementAtCaret = myFile.findElementAt(myEditor.getCaretModel().getOffset());
258 assertTrue(elementAtCaret instanceof PsiWhiteSpace);
260 TextRange dirty = myDaemonCodeAnalyzer.getFileStatusMap().getFileDirtyScope(document, Pass.UPDATE_ALL);
261 assertEquals(elementAtCaret.getTextRange(), dirty);
263 assertTrue(myDaemonCodeAnalyzer.isErrorAnalyzingFinished(getFile()));
267 public void testTypingSpaceInsideError() throws Exception {
268 configureByFile(BASE_PATH + "Error.java");
269 Collection<HighlightInfo> infos = highlightErrors();
270 assertEquals(1, infos.size());
272 for (int i = 0; i < 100; i++) {
274 List<HighlightInfo> errors = highlightErrors();
275 assertEquals(1, errors.size());
280 public void testBackSpaceInsideError() throws Exception {
281 configureByFile(BASE_PATH + "BackError.java");
282 Collection<HighlightInfo> infos = highlightErrors();
283 assertEquals(1, infos.size());
286 List<HighlightInfo> errors = highlightErrors();
287 assertEquals(1, errors.size());
291 protected LocalInspectionTool[] configureLocalInspectionTools() {
292 if (isPerformanceTest() && !getTestName(false).equals("TypingCurliesClearsEndOfFileErrorsInPhp_ItIsPerformanceTestBecauseItRunsTooLong")) {
293 // all possible inspections
294 List<InspectionToolWrapper> all = InspectionToolRegistrar.getInstance().createTools();
295 List<LocalInspectionTool> locals = new ArrayList<>();
296 all.stream().filter(tool -> tool instanceof LocalInspectionToolWrapper).forEach(tool -> {
297 LocalInspectionTool e = ((LocalInspectionToolWrapper)tool).getTool();
300 return locals.toArray(new LocalInspectionTool[locals.size()]);
302 return new LocalInspectionTool[]{
303 new FieldCanBeLocalInspection(),
304 new RequiredAttributesInspectionBase(),
305 new CheckDtdReferencesInspection(),
306 new AccessStaticViaInstance(),
311 public void testUnusedFieldUpdate() throws Exception {
312 configureByFile(BASE_PATH + "UnusedField.java");
313 Document document = getDocument(getFile());
314 List<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING);
315 assertEquals(1, infos.size());
316 assertEquals("Private field 'ffff' is never used", infos.get(0).getDescription());
318 type(" foo(ffff++);");
321 List<HighlightInfo> errors = DaemonCodeAnalyzerImpl.getHighlights(document, HighlightSeverity.WARNING, getProject());
322 assertEquals(0, errors.size());
325 public void testUnusedMethodUpdate() throws Exception {
326 configureByText(JavaFileType.INSTANCE, "class X {\n" +
327 " static void ffff() {}\n" +
328 " public static void main(String[] args){\n" +
329 " for (int i=0; i<1000;i++) {\n" +
330 " System.out.println(i);\n" +
331 " <caret>ffff();\n" +
334 enableInspectionTool(new UnusedDeclarationInspection(true));
335 List<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING);
339 infos = doHighlighting(HighlightSeverity.WARNING);
341 assertEquals(1, infos.size());
342 assertEquals("Method 'ffff()' is never used", infos.get(0).getDescription());
346 public void testAssignedButUnreadFieldUpdate() throws Exception {
347 configureByFile(BASE_PATH + "AssignedButUnreadField.java");
348 List<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING);
349 assertEquals(1, infos.size());
350 assertEquals("Private field 'text' is assigned but never accessed", infos.get(0).getDescription());
353 WriteCommandAction.runWriteCommandAction(getProject(), () -> {
354 EditorModificationUtil.deleteSelectedText(getEditor());
358 List<HighlightInfo> errors = doHighlighting(HighlightSeverity.WARNING);
359 assertEmpty(getFile().getText(), errors);
362 public void testDaemonIgnoresNonPhysicalEditor() throws Exception {
363 configureByFile(BASE_PATH + "AClass.java");
366 EditorFactory editorFactory = EditorFactory.getInstance();
367 final Document consoleDoc = editorFactory.createDocument("my console blah");
368 final Editor consoleEditor = editorFactory.createEditor(consoleDoc);
371 checkDaemonReaction(false, () -> caretRight(consoleEditor));
372 checkDaemonReaction(true, () -> typeInAlienEditor(consoleEditor, 'x'));
373 checkDaemonReaction(true, () -> LightPlatformCodeInsightTestCase.backspace(consoleEditor, getProject()));
376 checkDaemonReaction(true, this::caretRight);
379 editorFactory.releaseEditor(consoleEditor);
384 public void testDaemonIgnoresConsoleActivities() throws Exception {
385 configureByFile(BASE_PATH + "AClass.java");
386 doHighlighting(HighlightSeverity.WARNING);
388 final ConsoleView consoleView = TextConsoleBuilderFactory.getInstance().createBuilder(getProject()).getConsole();
390 consoleView.getComponent(); //create editor
391 consoleView.print("haha", ConsoleViewContentType.NORMAL_OUTPUT);
392 UIUtil.dispatchAllInvocationEvents();
395 checkDaemonReaction(false, () -> {
398 Thread.sleep(300); // *&^ing alarm
400 catch (InterruptedException e) {
403 UIUtil.dispatchAllInvocationEvents(); //flush
405 checkDaemonReaction(false, () -> {
406 consoleView.print("sss", ConsoleViewContentType.NORMAL_OUTPUT);
408 Thread.sleep(300); // *&^ing alarm
410 catch (InterruptedException e) {
413 UIUtil.dispatchAllInvocationEvents(); //flush
415 checkDaemonReaction(false, () -> {
416 consoleView.setOutputPaused(true);
418 Thread.sleep(300); // *&^ing alarm
420 catch (InterruptedException e) {
423 UIUtil.dispatchAllInvocationEvents(); //flush
427 Disposer.dispose(consoleView);
431 private void checkDaemonReaction(boolean mustCancelItself, @NotNull final Runnable action) {
432 PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
434 myDaemonCodeAnalyzer.waitForTermination();
435 TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(getEditor());
437 final AtomicBoolean run = new AtomicBoolean();
438 Disposable disposable = Disposer.newDisposable();
439 final AtomicReference<RuntimeException> stopDaemonReason = new AtomicReference<>();
440 StorageUtil.DEBUG_LOG = "";
441 getProject().getMessageBus().connect(disposable).subscribe(DaemonCodeAnalyzer.DAEMON_EVENT_TOPIC,
442 new DaemonCodeAnalyzer.DaemonListenerAdapter() {
444 public void daemonCancelEventOccurred(@NotNull String reason) {
445 stopDaemonReason.compareAndSet(null, new RuntimeException("Some bastard's restarted daemon: " + reason + "\nStorage write log: "+
446 StorageUtil.DEBUG_LOG));
452 myDaemonCodeAnalyzer.runPasses(getFile(), getDocument(getFile()), textEditor, new int[0], true, () -> {
453 if (!run.getAndSet(true)) {
459 catch (ProcessCanceledException ignored) { }
462 if (mustCancelItself) {
463 assertNotNull(stopDaemonReason.get());
466 if (stopDaemonReason.get() != null) throw stopDaemonReason.get();
470 StorageUtil.DEBUG_LOG = null;
471 Disposer.dispose(disposable);
476 public void testWholeFileInspection() throws Exception {
477 configureByFile(BASE_PATH + "FieldCanBeLocal.java");
478 List<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING);
479 assertEquals(1, infos.size());
480 assertEquals("Field can be converted to a local variable", infos.get(0).getDescription());
483 WriteCommandAction.runWriteCommandAction(getProject(), () -> {
484 EditorModificationUtil.deleteSelectedText(getEditor());
488 infos = doHighlighting(HighlightSeverity.WARNING);
492 WriteCommandAction.runWriteCommandAction(getProject(), () -> {
493 EditorModificationUtil.deleteSelectedText(getEditor());
497 infos = doHighlighting(HighlightSeverity.WARNING);
498 assertEquals(1, infos.size());
499 assertEquals("Field can be converted to a local variable", infos.get(0).getDescription());
502 private static class MyWholeInspection extends LocalInspectionTool {
503 private final List<PsiElement> visited = Collections.synchronizedList(new ArrayList<>());
508 public String getGroupDisplayName() {
515 public String getDisplayName() {
516 return getGroupDisplayName();
521 public String getShortName() {
522 return getGroupDisplayName();
527 public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
528 return new PsiElementVisitor() {
530 public void visitFile(PsiFile file) {
531 TimeoutUtil.sleep(1000); // make it run longer that LIP
532 super.visitFile(file);
536 public void visitElement(PsiElement element) {
537 visited.add(element);
538 super.visitElement(element);
544 public boolean runForWholeFile() {
549 public void testWholeFileInspectionRestartedOnAllElements() throws Exception {
550 MyWholeInspection tool = new MyWholeInspection();
551 enableInspectionTool(tool);
552 disposeOnTearDown(() -> disableInspectionTool(tool.getShortName()));
554 configureByText(JavaFileType.INSTANCE, "class X { void f() { <caret> } }");
555 List<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING);
557 int visitedCount = new HashSet<>(tool.visited).size();
558 tool.visited.clear();
560 type(" "); // white space modification
562 infos = doHighlighting(HighlightSeverity.WARNING);
565 int countAfter = new HashSet<>(tool.visited).size();
566 assertTrue("visitedCount = "+visitedCount+"; countAfter="+countAfter, countAfter >= visitedCount);
569 public void testWholeFileInspectionRestartedEvenIfThereWasAModificationInsideCodeBlockInOtherFile() throws Exception {
570 MyWholeInspection tool = new MyWholeInspection();
572 enableInspectionTool(tool);
573 disposeOnTearDown(() -> disableInspectionTool(tool.getShortName()));
575 PsiFile file = configureByText(JavaFileType.INSTANCE, "class X { void f() { <caret> } }");
576 PsiFile otherFile = createFile(myModule, file.getContainingDirectory().getVirtualFile(), "otherFile.txt", "xxx");
577 List<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING);
579 int visitedCount = tool.visited.size();
580 assertTrue(tool.visited.toString(), visitedCount > 0);
581 tool.visited.clear();
583 Document otherDocument = PsiDocumentManager.getInstance(getProject()).getDocument(otherFile);
584 WriteCommandAction.runWriteCommandAction(getProject(), () -> otherDocument.setText("zzz"));
586 infos = doHighlighting(HighlightSeverity.WARNING);
589 int countAfter = tool.visited.size();
590 assertTrue(tool.visited.toString(), countAfter > 0);
593 public void testOverriddenMethodMarkers() throws Exception {
594 configureByFile(BASE_PATH + getTestName(false) + ".java");
597 Document document = getEditor().getDocument();
598 List<LineMarkerInfo> markers = DaemonCodeAnalyzerImpl.getLineMarkers(document, getProject());
599 assertEquals(3, markers.size());
604 markers = DaemonCodeAnalyzerImpl.getLineMarkers(document, getProject());
605 assertEquals(3, markers.size());
609 public void testOverriddenMethodMarkersDoNotClearedByCHangingWhitespaceNearby() throws Exception {
610 configureByFile(BASE_PATH + "OverriddenMethodMarkers.java");
613 Document document = getEditor().getDocument();
614 List<LineMarkerInfo> markers = DaemonCodeAnalyzerImpl.getLineMarkers(document, getProject());
615 assertEquals(3, markers.size());
617 PsiElement element = ((PsiJavaFile)myFile).getClasses()[0].findMethodsByName("f", false)[0].getReturnTypeElement().getNextSibling();
618 assertEquals(" ", element.getText());
619 getEditor().getCaretModel().moveToOffset(element.getTextOffset() + 1);
623 markers = DaemonCodeAnalyzerImpl.getLineMarkers(document, getProject());
624 assertEquals(3, markers.size());
627 private static void commentLine() {
628 WriteCommandAction.runWriteCommandAction(null, () -> {
629 CommentByLineCommentAction action = new CommentByLineCommentAction();
630 action.actionPerformed(AnActionEvent.createFromAnAction(action, null, "", DataManager.getInstance().getDataContext()));
635 public void testChangeXmlIncludeLeadsToRehighlight() throws Exception {
636 LanguageFilter[] extensions = ((CompositeLanguage)StdLanguages.XML).getLanguageExtensions();
637 for (LanguageFilter extension : extensions) {
638 ((CompositeLanguage)StdLanguages.XML).unregisterLanguageExtension(extension);
641 final String location = getTestName(false) + ".xsd";
642 final String url = "http://myschema/";
643 ExternalResourceManagerExImpl.registerResourceTemporarily(url, location, getTestRootDisposable());
645 configureByFiles(null, BASE_PATH + getTestName(false) + ".xml", BASE_PATH + getTestName(false) + ".xsd");
647 Collection<HighlightInfo> errors = highlightErrors();
648 assertEquals(0, errors.size());
650 Editor[] allEditors = EditorFactory.getInstance().getAllEditors();
651 Editor schemaEditor = null;
652 for (Editor editor : allEditors) {
653 Document document = editor.getDocument();
654 PsiFile file = PsiDocumentManager.getInstance(getProject()).getPsiFile(document);
655 if (file == null) continue;
656 if (location.equals(file.getName())) {
657 schemaEditor = editor;
661 delete(schemaEditor);
663 errors = highlightErrors();
664 assertFalse(errors.isEmpty());
666 for (LanguageFilter extension : extensions) {
667 ((CompositeLanguage)StdLanguages.XML).registerLanguageExtension(extension);
672 public void testRehighlightInnerBlockAfterInline() throws Exception {
673 configureByFile(BASE_PATH + getTestName(false) + ".java");
675 Collection<HighlightInfo> errors = highlightErrors();
676 HighlightInfo error = assertOneElement(errors);
677 assertEquals("Variable 'e' is already defined in the scope", error.getDescription());
678 PsiElement element = getFile().findElementAt(getEditor().getCaretModel().getOffset()).getParent();
680 DataContext dataContext = SimpleDataContext.getSimpleContext(DataConstants.PSI_ELEMENT, element, ((EditorEx)getEditor()).getDataContext());
681 new InlineRefactoringActionHandler().invoke(getProject(), getEditor(), getFile(), dataContext);
683 Collection<HighlightInfo> afterTyping = highlightErrors();
684 assertEmpty(afterTyping);
688 public void testRangeMarkersDoNotGetAddedOrRemovedWhenUserIsJustTypingInsideHighlightedRegionAndEspeciallyInsideInjectedFragmentsWhichAreColoredGreenAndUsersComplainEndlesslyThatEditorFlickersThere()
690 configureByText(JavaFileType.INSTANCE, "class S { int f() {\n" +
691 " return <caret>hashCode();\n" +
694 Collection<HighlightInfo> infos = doHighlighting(HighlightInfoType.SYMBOL_TYPE_SEVERITY);
695 assertEquals(3, infos.size());
697 final int[] count = {0};
698 MarkupModelEx modelEx = (MarkupModelEx)DocumentMarkupModel.forDocument(getDocument(getFile()), getProject(), true);
699 modelEx.addMarkupModelListener(getTestRootDisposable(), new MarkupModelListener.Adapter() {
701 public void afterAdded(@NotNull RangeHighlighterEx highlighter) {
706 public void beforeRemoved(@NotNull RangeHighlighterEx highlighter) {
714 assertEquals(0, count[0]);
717 public void testLineMarkersReuse() throws Throwable {
718 configureByFile(BASE_PATH + "LineMarkerChange.java");
720 List<HighlightInfo> errors = highlightErrors();
723 List<LineMarkerInfo> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
724 assertSize(5, lineMarkers);
728 final Collection<String> changed = new ArrayList<>();
729 MarkupModelEx modelEx = (MarkupModelEx)DocumentMarkupModel.forDocument(getDocument(getFile()), getProject(), true);
730 modelEx.addMarkupModelListener(getTestRootDisposable(), new MarkupModelListener() {
732 public void afterAdded(@NotNull RangeHighlighterEx highlighter) {
733 changed(highlighter, ExceptionUtil.getThrowableText(new Throwable("after added")));
737 public void beforeRemoved(@NotNull RangeHighlighterEx highlighter) {
738 changed(highlighter, ExceptionUtil.getThrowableText(new Throwable("before removed")));
742 public void attributesChanged(@NotNull RangeHighlighterEx highlighter, boolean renderersChanged, boolean fontStyleChanged) {
743 changed(highlighter, ExceptionUtil.getThrowableText(new Throwable("changed")));
746 private void changed(@NotNull RangeHighlighterEx highlighter, String reason) {
747 if (highlighter.getTargetArea() != HighlighterTargetArea.LINES_IN_RANGE) return; // not line marker
748 List<LineMarkerInfo> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
749 if (ContainerUtil.find(lineMarkers, lm -> lm.highlighter == highlighter) == null) return; // not line marker
751 changed.add(highlighter+": \n"+reason);
755 PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
756 List<HighlightInfo> infosAfter = CodeInsightTestFixtureImpl.instantiateAndRun(myFile, myEditor, new int[]{/*Pass.UPDATE_ALL, Pass.LOCAL_INSPECTIONS*/}, false);
757 assertNotEmpty(filter(infosAfter, HighlightSeverity.ERROR));
759 assertEmpty(changed);
760 List<LineMarkerInfo> lineMarkersAfter = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
761 assertEquals(lineMarkersAfter.size(), lineMarkers.size());
764 public void testLineMarkersDoNotBlinkOnBackSpaceRightBeforeMethodIdentifier() throws Throwable {
765 configureByText(JavaFileType.INSTANCE, "package x; \n" +
766 "class <caret>ToRun{\n" +
767 " public static void main(String[] args) {\n"+
771 List<HighlightInfo> errors = highlightErrors();
774 List<LineMarkerInfo> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
775 assertSize(2, lineMarkers);
779 final Collection<String> changed = new ArrayList<>();
780 MarkupModelEx modelEx = (MarkupModelEx)DocumentMarkupModel.forDocument(getDocument(getFile()), getProject(), true);
781 modelEx.addMarkupModelListener(getTestRootDisposable(), new MarkupModelListener() {
783 public void afterAdded(@NotNull RangeHighlighterEx highlighter) {
784 changed(highlighter, ExceptionUtil.getThrowableText(new Throwable("after added")));
788 public void beforeRemoved(@NotNull RangeHighlighterEx highlighter) {
789 changed(highlighter, ExceptionUtil.getThrowableText(new Throwable("before removed")));
793 public void attributesChanged(@NotNull RangeHighlighterEx highlighter, boolean renderersChanged, boolean fontStyleChanged) {
794 changed(highlighter, ExceptionUtil.getThrowableText(new Throwable("changed")));
797 private void changed(@NotNull RangeHighlighterEx highlighter, String reason) {
798 if (highlighter.getTargetArea() != HighlighterTargetArea.LINES_IN_RANGE) return; // not line marker
799 List<LineMarkerInfo> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
800 if (ContainerUtil.find(lineMarkers, lm -> lm.highlighter == highlighter) == null) return; // not line marker
802 changed.add(highlighter+": \n"+reason);
806 assertEmpty(highlightErrors());
808 assertSize(2, DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject()));
810 assertEmpty(changed);
813 public void testTypeParametersMustNotBlinkWhenTypingInsideClass() throws Throwable {
814 configureByText(JavaFileType.INSTANCE, "package x; \n" +
815 "abstract class ToRun<TTTTTTTTTTTTTTT> implements Comparable<TTTTTTTTTTTTTTT> {\n" +
816 " private ToRun<TTTTTTTTTTTTTTT> delegate;\n"+
821 List<HighlightInfo> errors = highlightErrors();
824 MarkupModelEx modelEx = (MarkupModelEx)DocumentMarkupModel.forDocument(getDocument(getFile()), getProject(), true);
825 modelEx.addMarkupModelListener(getTestRootDisposable(), new MarkupModelListener.Adapter() {
827 public void beforeRemoved(@NotNull RangeHighlighterEx highlighter) {
828 if (TextRange.create(highlighter).substring(highlighter.getDocument().getText()).equals("TTTTTTTTTTTTTTT")) {
829 throw new RuntimeException("Must not remove type parameter highlighter");
834 assertEmpty(highlightErrors());
837 assertEmpty(highlightErrors());
839 assertEmpty(highlightErrors());
841 assertEmpty(highlightErrors());
843 assertEmpty(highlightErrors());
846 assertEmpty(highlightErrors());
849 public void testOverrideMethodsHighlightingPersistWhenTypeInsideMethodBody() throws Throwable {
850 configureByText(JavaFileType.INSTANCE, "package x; \n" +
852 " static <T> void sayHello(Class<? extends T> msg) {}\n" +
855 "class ClassB extends ClassA {\n" +
856 " static <T extends String> void sayHello(Class<? extends T> msg) {<caret>\n" +
860 assertSize(1, highlightErrors());
861 type("//my comment inside method body, so class modifier won't be visited");
862 assertSize(1, highlightErrors());
865 public void testLineMarkersClearWhenTypingAtTheEndOfPsiComment() throws Throwable {
866 configureByText(JavaFileType.INSTANCE, "class S {\n//ddd<caret>\n}");
867 StringBuffer log = new StringBuffer();
868 final LineMarkerProvider provider = new LineMarkerProvider() {
871 public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement element) {
872 log.append("getLineMarkerInfo(" + element + ")\n");
873 if (element instanceof PsiComment) {
874 LineMarkerInfo<PsiComment> info = new LineMarkerInfo<>((PsiComment)element, element.getTextRange(), null, Pass.UPDATE_ALL, null, null, GutterIconRenderer.Alignment.LEFT);
875 log.append(info + "\n");
882 public void collectSlowLineMarkers(@NotNull List<PsiElement> elements, @NotNull Collection<LineMarkerInfo> result) {
886 LineMarkerProviders.INSTANCE.addExplicitExtension(JavaLanguage.INSTANCE, provider);
887 Disposer.register(myTestRootDisposable, () -> LineMarkerProviders.INSTANCE.removeExplicitExtension(JavaLanguage.INSTANCE, provider));
888 myDaemonCodeAnalyzer.restart();
890 List<HighlightInfo> infos = doHighlighting();
891 log.append("File text: '" + getFile().getText() + "'\n");
892 log.append("infos: " + infos + "\n");
893 assertEmpty(filter(infos,HighlightSeverity.ERROR));
895 List<LineMarkerInfo> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
896 assertOneElement(lineMarkers);
899 infos = doHighlighting();
900 log.append("File text: '" + getFile().getText() + "'\n");
901 log.append("infos: " + infos + "\n");
902 assertEmpty(filter(infos,HighlightSeverity.ERROR));
904 lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
905 assertOneElement(lineMarkers);
908 infos = doHighlighting();
909 log.append("File text: '" + getFile().getText() + "'\n");
910 log.append("infos: " + infos + "\n");
911 assertEmpty(filter(infos,HighlightSeverity.ERROR));
913 lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(myEditor.getDocument(), getProject());
914 assertOneElement(lineMarkers);
916 catch (AssertionError e) {
917 System.err.println("Log:\n"+log+"\n---");
922 public void testWhenTypingOverWrongReferenceItsColorChangesToBlackAndOnlyAfterHighlightingFinishedItReturnsToRed() throws Throwable {
923 configureByText(StdFileTypes.JAVA, "class S { int f() {\n" +
924 " return asfsdfsdfsd<caret>;\n" +
927 Collection<HighlightInfo> errors = highlightErrors();
928 assertOneElement(errors);
929 assertSame(HighlightInfoType.WRONG_REF, errors.iterator().next().type);
931 Document document = getDocument(getFile());
935 List<HighlightInfo> infos = DaemonCodeAnalyzerImpl.getHighlights(document, HighlightInfoType.SYMBOL_TYPE_SEVERITY, getProject());
936 for (HighlightInfo info : infos) {
937 assertNotSame(HighlightInfoType.WRONG_REF, info.type);
940 errors = highlightErrors();
941 assertOneElement(errors);
942 assertSame(HighlightInfoType.WRONG_REF, errors.iterator().next().type);
946 public void testQuickFixRemainsAvailableAfterAnotherFixHasBeenAppliedInTheSameCodeBlockBefore() throws Exception {
947 configureByFile(BASE_PATH + "QuickFixes.java");
949 DaemonCodeAnalyzerSettings settings = DaemonCodeAnalyzerSettings.getInstance();
950 boolean old = settings.NEXT_ERROR_ACTION_GOES_TO_ERRORS_FIRST;
951 settings.NEXT_ERROR_ACTION_GOES_TO_ERRORS_FIRST = true;
954 Collection<HighlightInfo> errors = highlightErrors();
955 assertEquals(3, errors.size());
956 new GotoNextErrorHandler(true).invoke(getProject(), getEditor(), getFile());
958 List<IntentionAction> fixes = LightQuickFixTestCase.getAvailableActions(getEditor(), getFile());
959 IntentionAction fix = assertContainsOneOf(fixes, DeleteCatchFix.class);
960 assertEquals("Delete catch for 'java.io.IOException'", fix.getText());
962 final IntentionAction finalFix = fix;
963 WriteCommandAction.runWriteCommandAction(getProject(), () -> finalFix.invoke(getProject(), getEditor(), getFile()));
965 errors = highlightErrors();
966 assertEquals(2, errors.size());
968 new GotoNextErrorHandler(true).invoke(getProject(), getEditor(), getFile());
969 fixes = LightQuickFixTestCase.getAvailableActions(getEditor(), getFile());
970 fix = assertContainsOneOf(fixes, DeleteCatchFix.class);
971 assertEquals("Delete catch for 'java.io.IOException'", fix.getText());
973 final IntentionAction finalFix1 = fix;
974 WriteCommandAction.runWriteCommandAction(getProject(), () -> finalFix1.invoke(getProject(), getEditor(), getFile()));
976 errors = highlightErrors();
977 assertOneElement(errors);
979 new GotoNextErrorHandler(true).invoke(getProject(), getEditor(), getFile());
980 fixes = LightQuickFixTestCase.getAvailableActions(getEditor(), getFile());
981 fix = assertContainsOneOf(fixes, DeleteCatchFix.class);
982 assertEquals("Delete catch for 'java.io.IOException'", fix.getText());
984 final IntentionAction finalFix2 = fix;
985 WriteCommandAction.runWriteCommandAction(getProject(), () -> finalFix2.invoke(getProject(), getEditor(), getFile()));
987 errors = highlightErrors();
991 settings.NEXT_ERROR_ACTION_GOES_TO_ERRORS_FIRST = old;
995 private static <T> T assertContainsOneOf(@NotNull Collection<T> collection, @NotNull Class<?> aClass) {
997 for (T t : collection) {
998 if (aClass.isInstance(t)) {
999 if (result != null) {
1000 fail("multiple " + aClass.getName() + " objects present in collection " + collection);
1007 assertNotNull(aClass.getName() + " object not found in collection " + collection, result);
1012 public void testRangeHighlightersDoNotGetStuckForever() throws Throwable {
1013 configureByText(StdFileTypes.JAVA, "class S { void ffffff() {fff<caret>fff();}}");
1015 List<HighlightInfo> infos = highlightErrors();
1017 MarkupModel markup = DocumentMarkupModel.forDocument(myEditor.getDocument(), getProject(), true);
1018 final TextRange[] highlightersBefore = getHighlightersTextRange(markup);
1026 infos = highlightErrors();
1029 final TextRange[] highlightersAfter = getHighlightersTextRange(markup);
1031 assertEquals(highlightersBefore.length, highlightersAfter.length);
1032 for (int i = 0; i < highlightersBefore.length; i++) {
1033 TextRange before = highlightersBefore[i];
1034 TextRange after = highlightersAfter[i];
1035 assertEquals(before.getStartOffset(), after.getStartOffset());
1036 assertEquals(before.getEndOffset(), after.getEndOffset());
1041 private static TextRange[] getHighlightersTextRange(@NotNull MarkupModel markup) {
1042 final RangeHighlighter[] highlighters = markup.getAllHighlighters();
1044 final TextRange[] result = new TextRange[highlighters.length];
1045 for (int i = 0; i < highlighters.length; i++) {
1046 result[i] = ProperTextRange.create(highlighters[i]);
1048 return orderByHashCode(result); // markup.getAllHighlighters returns unordered array
1052 private static <T extends Segment> T[] orderByHashCode(@NotNull T[] highlighters) {
1053 Arrays.sort(highlighters, (o1, o2) -> o2.hashCode() - o1.hashCode());
1054 return highlighters;
1057 public void testFileStatusMapDirtyCachingWorks() throws Throwable {
1058 myDaemonCodeAnalyzer.setUpdateByTimerEnabled(false); // to prevent auto-start highlighting
1059 UIUtil.dispatchAllInvocationEvents();
1060 configureByText(StdFileTypes.JAVA, "class <caret>S { int ffffff = 0;}");
1061 UIUtil.dispatchAllInvocationEvents();
1063 final int[] creation = {0};
1064 class Fac extends AbstractProjectComponent implements TextEditorHighlightingPassFactory {
1065 private Fac(Project project) {
1070 public TextEditorHighlightingPass createHighlightingPass(@NotNull PsiFile file, @NotNull Editor editor) {
1071 TextRange textRange = FileStatusMap.getDirtyTextRange(editor, Pass.UPDATE_ALL);
1072 if (textRange == null) return null;
1073 return new MyPass(myProject);
1076 class MyPass extends TextEditorHighlightingPass {
1077 private MyPass(final Project project) {
1078 super(project, getEditor().getDocument(), false);
1083 public void doCollectInformation(@NotNull ProgressIndicator progress) {
1087 public void doApplyInformationToEditor() {
1091 TextEditorHighlightingPassRegistrar registrar = TextEditorHighlightingPassRegistrar.getInstance(getProject());
1092 registrar.registerTextEditorHighlightingPass(new Fac(getProject()), null, null, false, -1);
1094 assertEquals(1, creation[0]);
1098 assertEquals(1, creation[0]);
1100 assertEquals(1, creation[0]);
1104 assertEquals(2, creation[0]);
1106 assertEquals(2, creation[0]);
1108 assertEquals(2, creation[0]);
1112 public void testDefensivelyDirtyFlagDoesNotClearPrematurely() throws Throwable {
1113 class Fac extends AbstractProjectComponent implements TextEditorHighlightingPassFactory {
1114 private Fac(Project project) {
1119 public TextEditorHighlightingPass createHighlightingPass(@NotNull PsiFile file, @NotNull Editor editor) {
1123 TextEditorHighlightingPassRegistrar registrar = TextEditorHighlightingPassRegistrar.getInstance(getProject());
1124 registrar.registerTextEditorHighlightingPass(new Fac(getProject()), null, null, false, -1);
1126 configureByText(StdFileTypes.JAVA, "@Deprecated<caret> class S { } ");
1128 List<HighlightInfo> infos = doHighlighting(HighlightInfoType.SYMBOL_TYPE_SEVERITY);
1129 assertEquals(2, infos.size());
1131 assertEquals("@Deprecated", infos.get(0).getText());
1132 assertEquals("S", infos.get(1).getText());
1137 List<HighlightInfo> after = doHighlighting(HighlightInfoType.SYMBOL_TYPE_SEVERITY);
1139 assertEquals("@Deprecated", after.get(0).getText());
1140 assertEquals("S", after.get(1).getText());
1145 getEditor().getCaretModel().moveToOffset(getEditor().getDocument().getTextLength());
1148 after = doHighlighting(HighlightInfoType.SYMBOL_TYPE_SEVERITY);
1149 assertEquals(2, after.size());
1151 assertEquals("@Deprecated", after.get(0).getText());
1152 assertEquals("S", after.get(1).getText());
1156 public void testModificationInsideCodeblockDoesnotAffectErrorMarkersOutside() throws Exception {
1157 configureByFile(BASE_PATH + "ErrorMark.java");
1158 List<HighlightInfo> errs = highlightErrors();
1159 assertEquals(1, errs.size());
1160 assertEquals("'}' expected", errs.get(0).getDescription());
1163 errs = highlightErrors();
1164 assertEquals(1, errs.size());
1165 assertEquals("'}' expected", errs.get(0).getDescription());
1168 public void testErrorMarkerAtTheEndOfTheFile() throws Exception {
1169 CommandProcessor.getInstance().executeCommand(getProject(), () -> {
1171 configureByFile(BASE_PATH + "ErrorMarkAtEnd.java");
1173 catch (Exception e) {
1177 List<HighlightInfo> errs = highlightErrors();
1179 CommandProcessor.getInstance().executeCommand(getProject(), () -> {
1180 Document document = getEditor().getDocument();
1181 int offset = getEditor().getCaretModel().getOffset();
1182 while (offset < document.getTextLength()) {
1183 int i = StringUtil.indexOf(document.getText(), '}', offset, document.getTextLength());
1185 getEditor().getCaretModel().moveToOffset(i);
1186 delete(getEditor());
1190 errs = highlightErrors();
1191 assertEquals(2, errs.size());
1192 assertEquals("'}' expected", errs.get(0).getDescription());
1195 errs = highlightErrors();
1201 public void _testSOEInEndlessAppendChainPerformance() throws Throwable {
1202 StringBuilder text = new StringBuilder("class S { String ffffff = new StringBuilder()\n");
1203 for (int i=0; i<2000; i++) {
1204 text.append(".append(").append(i).append(")\n");
1206 text.append(".toString();<caret>}");
1207 configureByText(StdFileTypes.JAVA, text.toString());
1209 PlatformTestUtil.startPerformanceTest("too many tree visitors", 30000, () -> {
1210 List<HighlightInfo> infos = highlightErrors();
1213 List<HighlightInfo> errors = highlightErrors();
1214 assertFalse(errors.isEmpty());
1221 infos = highlightErrors();
1223 }).useLegacyScaling().assertTiming();
1227 public void testBulbAppearsAfterType() throws Throwable {
1228 String text = "class S { ArrayList<caret>XXX x;}";
1229 configureByText(StdFileTypes.JAVA, text);
1231 ((EditorImpl)myEditor).getScrollPane().getViewport().setSize(1000, 1000);
1232 DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true);
1234 final Set<LightweightHint> shown = ContainerUtil.newIdentityTroveSet();
1235 getProject().getMessageBus().connect().subscribe(EditorHintListener.TOPIC, (project, hint, flags) -> {
1237 hint.addHintListener(event -> shown.remove(hint));
1240 DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
1243 IntentionHintComponent hintComponent = codeAnalyzer.getLastIntentionHint();
1244 assertNotNull(hintComponent);
1245 assertFalse(hintComponent.isDisposed());
1246 assertNotNull(hintComponent.getComponentHint());
1247 assertTrue(shown.contains(hintComponent.getComponentHint()));
1251 hintComponent = codeAnalyzer.getLastIntentionHint();
1252 assertNotNull(hintComponent);
1253 assertFalse(hintComponent.isDisposed());
1254 assertNotNull(hintComponent.getComponentHint());
1255 assertTrue(shown.contains(hintComponent.getComponentHint()));
1259 protected void configureByExistingFile(@NotNull VirtualFile virtualFile) {
1260 super.configureByExistingFile(virtualFile);
1261 EditorTracker editorTracker = getProject().getComponent(EditorTracker.class);
1262 editorTracker.setActiveEditors(Collections.singletonList(getEditor()));
1266 protected VirtualFile configureByFiles(@Nullable File rawProjectRoot, @NotNull VirtualFile... vFiles) throws IOException {
1267 VirtualFile file = super.configureByFiles(rawProjectRoot, vFiles);
1268 EditorTracker editorTracker = getProject().getComponent(EditorTracker.class);
1269 editorTracker.setActiveEditors(Collections.singletonList(getEditor()));
1273 public void testDaemonIgnoresFrameDeactivation() throws Throwable {
1274 DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true); // return default value to avoid unnecessary save
1275 InspectionProfileManager.getInstance().setRootProfile(InspectionProfileImpl.getDefaultProfile().getName()); // reset to default profile from the custom one to avoid unnecessary save
1277 String text = "class S { ArrayList<caret>XXX x;}";
1278 configureByText(StdFileTypes.JAVA, text);
1281 GeneralSettings settings = GeneralSettings.getInstance();
1282 ApplicationEx application = ApplicationManagerEx.getApplicationEx();
1283 boolean frameSave = settings.isSaveOnFrameDeactivation();
1284 boolean appSave = application.isDoNotSave();
1286 settings.setSaveOnFrameDeactivation(true);
1287 application.doNotSave(false);
1289 SaveAndSyncHandlerImpl.doSaveDocumentsAndProjectsAndApp();
1291 checkDaemonReaction(false, SaveAndSyncHandlerImpl::doSaveDocumentsAndProjectsAndApp);
1294 application.doNotSave(appSave);
1295 settings.setSaveOnFrameDeactivation(frameSave);
1300 public void testApplyLocalQuickFix() throws Throwable {
1301 configureByText(StdFileTypes.JAVA, "class X { static int sss; public int f() { return this.<caret>sss; }}");
1303 ((EditorImpl)myEditor).getScrollPane().getViewport().setSize(1000, 1000);
1304 DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true);
1306 List<HighlightInfo> warns = doHighlighting(HighlightSeverity.WARNING);
1307 assertOneElement(warns);
1308 List<HighlightInfo.IntentionActionDescriptor> actions = ShowIntentionsPass.getAvailableActions(getEditor(), getFile(), -1);
1309 final HighlightInfo.IntentionActionDescriptor descriptor = assertOneElement(actions);
1310 WriteCommandAction.runWriteCommandAction(getProject(), () -> descriptor.getAction().invoke(getProject(), getEditor(), getFile()));
1313 actions = ShowIntentionsPass.getAvailableActions(getEditor(), getFile(), -1);
1314 assertEmpty(actions);
1318 public void testApplyErrorInTheMiddle() throws Throwable {
1319 String text = "class <caret>X { ";
1320 for (int i = 0; i < 100; i++) {
1322 "// String x = \"<zzzzzzzzzz/>\";\n" +
1326 configureByText(StdFileTypes.JAVA, text);
1328 ((EditorImpl)myEditor).getScrollPane().getViewport().setSize(1000, 1000);
1329 DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true);
1331 List<HighlightInfo> warns = highlightErrors();
1335 List<HighlightInfo> errors = highlightErrors();
1336 assertEquals(2, errors.size());
1341 errors = highlightErrors();
1342 assertEmpty(errors);
1346 public void testErrorInTheEndOutsideVisibleArea() throws Throwable {
1347 String text = "<xml> \n" + StringUtil.repeatSymbol('\n', 1000) + "</xml>\nxxxxx<caret>";
1348 configureByText(StdFileTypes.XML, text);
1350 ProperTextRange visibleRange = makeEditorWindowVisible(new Point(0, 1000));
1351 assertTrue(visibleRange.getStartOffset() > 0);
1353 List<HighlightInfo> warns = highlightErrors();
1354 HighlightInfo info = assertOneElement(warns);
1355 assertEquals("Top level element is not completed", info.getDescription());
1358 List<HighlightInfo> errors = highlightErrors();
1359 info = assertOneElement(errors);
1360 assertEquals("Top level element is not completed", info.getDescription());
1363 private ProperTextRange makeEditorWindowVisible(Point viewPosition) {
1364 ((EditorImpl)myEditor).getScrollPane().getViewport().setSize(1000, 1000);
1365 DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true);
1367 myEditor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
1368 ((EditorImpl)myEditor).getScrollPane().getViewport().setViewPosition(viewPosition);
1369 ((EditorImpl)myEditor).getScrollPane().getViewport().setExtentSize(new Dimension(100, ((EditorImpl)myEditor).getPreferredHeight() -
1371 return VisibleHighlightingPassFactory.calculateVisibleRange(getEditor());
1375 public void testEnterInCodeBlock() throws Throwable {
1376 String text = "class LQF {\n" +
1377 " int wwwwwwwwwwww;\n" +
1378 " public void main() {<caret>\n" +
1379 " wwwwwwwwwwww = 1;\n" +
1382 configureByText(StdFileTypes.JAVA, text);
1384 ((EditorImpl)myEditor).getScrollPane().getViewport().setSize(1000, 1000);
1386 List<HighlightInfo> infos = doHighlighting(HighlightInfoType.SYMBOL_TYPE_SEVERITY);
1387 assertEquals(4, infos.size());
1390 infos = doHighlighting(HighlightInfoType.SYMBOL_TYPE_SEVERITY);
1391 assertEquals(4, infos.size());
1395 infos = doHighlighting(HighlightInfoType.SYMBOL_TYPE_SEVERITY);
1396 assertEquals(4, infos.size());
1400 public void testTypingNearEmptyErrorElement() throws Throwable {
1401 String text = "class LQF {\n" +
1402 " public void main() {\n" +
1403 " int wwwwwwwwwwww = 1<caret>\n" +
1406 configureByText(StdFileTypes.JAVA, text);
1408 ((EditorImpl)myEditor).getScrollPane().getViewport().setSize(1000, 1000);
1410 List<HighlightInfo> infos = highlightErrors();
1411 assertEquals(1, infos.size());
1414 infos = highlightErrors();
1419 public void testLIPGetAllParentsAfterCodeBlockModification() throws Throwable {
1420 String text = "class LQF {\n" +
1422 " public void me() {\n" +
1426 configureByText(StdFileTypes.JAVA, text);
1428 final List<PsiElement> visitedElements = Collections.synchronizedList(new ArrayList<>());
1429 class MyVisitor extends PsiElementVisitor {
1431 public void visitElement(PsiElement element) {
1432 visitedElements.add(element);
1435 final MyVisitor visitor = new MyVisitor();
1436 final LocalInspectionTool tool = new LocalInspectionTool() {
1440 public String getGroupDisplayName() {
1447 public String getDisplayName() {
1448 return getGroupDisplayName();
1453 public String getShortName() {
1454 return getGroupDisplayName();
1459 public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
1463 disposeOnTearDown(() -> disableInspectionTool(tool.getShortName()));
1464 enableInspectionTool(tool);
1466 List<HighlightInfo> infos = highlightErrors();
1469 List<PsiElement> allPsi = CollectHighlightsUtil.getElementsInRange(myFile, 0, myFile.getTextLength());
1470 assertEquals(new HashSet<>(allPsi), new HashSet<>(visitedElements));
1472 // inside code block modification
1473 visitedElements.clear();
1476 infos = highlightErrors();
1480 PsiMethod method = ((PsiJavaFile)myFile).getClasses()[0].getMethods()[0];
1481 List<PsiElement> methodAndParents =
1482 CollectHighlightsUtil.getElementsInRange(myFile, method.getTextRange().getStartOffset(), method.getTextRange().getEndOffset(), true);
1483 assertEquals(new HashSet<>(methodAndParents), new HashSet<>(visitedElements));
1487 public void testCancelsItSelfOnTypingInAlienProject() throws Throwable {
1488 String body = StringUtil.repeat("\"String field = null;\"\n", 1000);
1489 configureByText(StdFileTypes.JAVA, "class X{ void f() {" + body + "<caret>\n} }");
1491 File temp = createTempDirectory();
1492 final Project alienProject = createProject(temp + "/alien.ipr", DebugUtil.currentStackTrace());
1493 boolean succ2 = ProjectManagerEx.getInstanceEx().openProject(alienProject);
1495 DaemonProgressIndicator.setDebug(true);
1496 final DaemonProgressIndicator[] indicator = new DaemonProgressIndicator[1];
1499 Module alienModule = doCreateRealModuleIn("x", alienProject, getModuleType());
1500 final VirtualFile alienRoot = PsiTestUtil.createTestProjectStructure(alienProject, alienModule, myFilesToDelete);
1501 OpenFileDescriptor alienDescriptor = new WriteAction<OpenFileDescriptor>() {
1503 protected void run(@NotNull Result<OpenFileDescriptor> result) throws Throwable {
1504 VirtualFile alienFile = alienRoot.createChildData(this, "X.java");
1505 setFileText(alienFile, "class Alien { }");
1506 OpenFileDescriptor alienDescriptor = new OpenFileDescriptor(alienProject, alienFile);
1507 result.setResult(alienDescriptor);
1509 }.execute().throwException().getResultObject();
1511 FileEditorManager fe = FileEditorManager.getInstance(alienProject);
1512 final Editor alienEditor = fe.openTextEditor(alienDescriptor, false);
1513 ((EditorImpl)alienEditor).setCaretActive();
1514 PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
1515 PsiDocumentManager.getInstance(alienProject).commitAllDocuments();
1517 // start daemon in main project. should check for its cancel when typing in alien
1518 TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(getEditor());
1519 DaemonCodeAnalyzerImpl di = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
1520 final boolean[] checked = {false};
1521 di.runPasses(getFile(), getEditor().getDocument(), textEditor, ArrayUtil.EMPTY_INT_ARRAY, true, () -> {
1522 if (checked[0]) return;
1524 indicator[0] = myDaemonCodeAnalyzer.getUpdateProgress();
1525 typeInAlienEditor(alienEditor, 'x');
1528 catch (ProcessCanceledException ignored) {
1529 //DaemonProgressIndicator.setDebug(true);
1530 //System.out.println("indicator = " + indicator[0]);
1534 ProjectManagerEx.getInstanceEx().closeAndDispose(alienProject);
1536 fail("must throw PCE");
1539 public void testPasteInAnonymousCodeBlock() throws Throwable {
1540 configureByText(StdFileTypes.JAVA, "class X{ void f() {" +
1542 " Runnable r = new Runnable() { public void run() {\n" +
1545 " <selection>int y = x;</selection>\n " +
1547 assertEmpty(highlightErrors());
1549 assertEquals("int y = x;", getEditor().getSelectionModel().getSelectedText());
1550 getEditor().getSelectionModel().removeSelection();
1552 List<HighlightInfo> errors = highlightErrors();
1553 assertEquals(1, errors.size());
1556 private void paste() {
1557 EditorActionManager actionManager = EditorActionManager.getInstance();
1558 final EditorActionHandler actionHandler = actionManager.getActionHandler(IdeActions.ACTION_EDITOR_PASTE);
1559 WriteCommandAction.runWriteCommandAction(null, () -> actionHandler.execute(getEditor(), null, DataManager.getInstance().getDataContext()));
1562 private void copy() {
1563 EditorActionManager actionManager = EditorActionManager.getInstance();
1564 final EditorActionHandler actionHandler = actionManager.getActionHandler(IdeActions.ACTION_EDITOR_COPY);
1565 WriteCommandAction.runWriteCommandAction(null, () -> actionHandler.execute(getEditor(), null, DataManager.getInstance().getDataContext()));
1568 public void testReactivityPerformance() throws Throwable {
1569 @NonNls String filePath = "/psi/resolve/Thinlet.java";
1570 configureByFile(filePath);
1572 CompletionContributor.forLanguage(getFile().getLanguage());
1573 long s = System.currentTimeMillis();
1575 long e = System.currentTimeMillis();
1576 System.out.println("Hi elapsed: "+(e-s));
1578 final DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
1579 int N = Math.max(5, Timings.adjustAccordingToMySpeed(80, true));
1580 System.out.println("N = " + N);
1581 final long[] interruptTimes = new long[N];
1582 List<Thread> watchers = new ArrayList<>();
1583 for (int i = 0; i < N; i++) {
1584 codeAnalyzer.restart();
1585 final int finalI = i;
1586 final long start = System.currentTimeMillis();
1587 Runnable interrupt = () -> {
1588 long now = System.currentTimeMillis();
1589 if (now - start < 100) {
1590 // wait to engage all highlighting threads
1593 final AtomicLong typingStart = new AtomicLong();
1594 final DaemonProgressIndicator progress = codeAnalyzer.getUpdateProgress();
1595 Thread watcher = new Thread("reactivity watcher") {
1599 final long start1 = typingStart.get();
1600 if (start1 == -1) break;
1605 catch (InterruptedException e1) {
1606 throw new RuntimeException(e1);
1610 long now = System.currentTimeMillis();
1611 if (now - start1 > 500) {
1612 // too long, see WTF
1613 PerformanceWatcher.dumpThreadsToConsole("Too long interrupt: " +
1615 "; Progress canceled=" +
1616 progress.isCanceled() +
1617 "\n----------------------------");
1618 System.err.println("----all threads---");
1619 for (Thread thread : Thread.getAllStackTraces().keySet()) {
1620 boolean canceled = CoreProgressManager.isCanceledThread(thread);
1622 System.err.println("Thread " + thread + " is canceled");
1625 System.err.println("----///////---");
1632 watchers.add(watcher);
1633 typingStart.set(System.currentTimeMillis());
1635 typingStart.set(-1);
1636 long end = System.currentTimeMillis();
1637 long interruptTime = end - now;
1638 interruptTimes[finalI] = interruptTime;
1639 assertNull(codeAnalyzer.getUpdateProgress());
1640 System.out.println(interruptTime);
1641 throw new ProcessCanceledException();
1644 PsiFile file = getFile();
1645 Editor editor = getEditor();
1646 Project project = file.getProject();
1647 CodeInsightTestFixtureImpl.ensureIndexesUpToDate(project);
1648 TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(editor);
1649 PsiDocumentManager.getInstance(myProject).commitAllDocuments();
1650 codeAnalyzer.runPasses(file, editor.getDocument(), textEditor, ArrayUtil.EMPTY_INT_ARRAY, false, interrupt);
1651 DaemonProgressIndicator progress = codeAnalyzer.getUpdateProgress();
1652 throw new RuntimeException("should have been interrupted: "+progress);
1654 catch (ProcessCanceledException ignored) {
1658 long ave = ArrayUtil.averageAmongMedians(interruptTimes, 3);
1659 System.out.println("Average among the N/3 median times: " + ave + "ms");
1660 assertTrue(ave < 300);
1661 for (Thread watcher : watchers) {
1666 public void testTypingLatencyPerformance() throws Throwable {
1667 @NonNls String filePath = "/psi/resolve/ThinletBig.java";
1669 configureByFile(filePath);
1672 CompletionContributor.forLanguage(getFile().getLanguage());
1673 long s = System.currentTimeMillis();
1675 long e = System.currentTimeMillis();
1676 //System.out.println("Hi elapsed: "+(e-s));
1678 final DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
1679 int N = Math.max(5, Timings.adjustAccordingToMySpeed(80, true));
1680 //System.out.println("N = " + N);
1681 final long[] interruptTimes = new long[N];
1682 for (int i = 0; i < N; i++) {
1683 codeAnalyzer.restart();
1684 final int finalI = i;
1685 final long start = System.currentTimeMillis();
1686 Runnable interrupt = () -> {
1687 long now = System.currentTimeMillis();
1688 if (now - start < 100) {
1689 // wait to engage all highlighting threads
1693 long end = System.currentTimeMillis();
1694 long interruptTime = end - now;
1695 interruptTimes[finalI] = interruptTime;
1696 assertNull(codeAnalyzer.getUpdateProgress());
1697 //System.out.println(interruptTime);
1698 throw new ProcessCanceledException();
1701 PsiFile file = getFile();
1702 Editor editor = getEditor();
1703 Project project = file.getProject();
1704 CodeInsightTestFixtureImpl.ensureIndexesUpToDate(project);
1705 TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(editor);
1706 PsiDocumentManager.getInstance(myProject).commitAllDocuments();
1707 codeAnalyzer.runPasses(file, editor.getDocument(), textEditor, ArrayUtil.EMPTY_INT_ARRAY, false, interrupt);
1709 throw new RuntimeException("should have been interrupted");
1711 catch (ProcessCanceledException ignored) {
1714 //highlightErrors();
1717 long mean = ArrayUtil.averageAmongMedians(interruptTimes, 3);
1718 long avg = Arrays.stream(interruptTimes).sum() / interruptTimes.length;
1719 long max = Arrays.stream(interruptTimes).max().getAsLong();
1720 long min = Arrays.stream(interruptTimes).min().getAsLong();
1721 System.out.println("Average among the N/3 median times: " + mean + "ms; max: "+max+"; min:"+min+"; avg: "+avg);
1722 assertTrue(mean < 10);
1725 private static void startCPUProfiling() {
1727 Class<?> aClass = Class.forName("com.intellij.util.ProfilingUtil");
1728 Method method = ReflectionUtil.getDeclaredMethod(aClass, "startCPUProfiling");
1729 method.invoke(null);
1731 catch (Exception e) {
1732 throw new RuntimeException(e);
1735 private static void stopCPUProfiling() {
1737 Class<?> aClass = Class.forName("com.intellij.util.ProfilingUtil");
1738 Method method = ReflectionUtil.getDeclaredMethod(aClass, "stopCPUProfiling");
1739 method.invoke(null);
1741 catch (Exception e) {
1742 throw new RuntimeException(e);
1746 private static String captureCPUSnapshot() {
1748 Class<?> aClass = Class.forName("com.intellij.util.ProfilingUtil");
1749 Method method = ReflectionUtil.getDeclaredMethod(aClass, "captureCPUSnapshot");
1750 return (String)method.invoke(null);
1752 catch (Exception e) {
1753 throw new RuntimeException(e);
1757 public void testPostHighlightingPassRunsOnEveryPsiModification() throws Exception {
1758 PsiFile x = createFile("X.java", "public class X { public static void ffffffffffffff(){} }");
1759 PsiFile use = createFile("Use.java", "public class Use { { <caret>X.ffffffffffffff(); } }");
1760 configureByExistingFile(use.getVirtualFile());
1762 InspectionProfile profile = InspectionProjectProfileManager.getInstance(myProject).getCurrentProfile();
1763 HighlightDisplayKey myDeadCodeKey = HighlightDisplayKey.find(UnusedDeclarationInspectionBase.SHORT_NAME);
1764 if (myDeadCodeKey == null) {
1765 myDeadCodeKey = HighlightDisplayKey.register(UnusedDeclarationInspectionBase.SHORT_NAME, UnusedDeclarationInspectionBase.DISPLAY_NAME);
1767 UnusedDeclarationInspectionBase myDeadCodeInspection = new UnusedDeclarationInspectionBase(true);
1768 enableInspectionTool(myDeadCodeInspection);
1769 assert profile.isToolEnabled(myDeadCodeKey, myFile);
1771 Editor xEditor = createEditor(x.getVirtualFile());
1772 List<HighlightInfo> xInfos = filter(CodeInsightTestFixtureImpl.instantiateAndRun(x, xEditor, new int[0], false),
1773 HighlightSeverity.WARNING);
1774 HighlightInfo info = ContainerUtil.find(xInfos, xInfo -> xInfo.getDescription().equals("Method 'ffffffffffffff()' is never used"));
1775 assertNull(xInfos.toString(), info);
1777 Editor useEditor = myEditor;
1778 List<HighlightInfo> useInfos = filter(CodeInsightTestFixtureImpl.instantiateAndRun(use, useEditor, new int[0], false), HighlightSeverity.ERROR);
1779 assertEmpty(useInfos);
1784 PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
1785 xInfos = filter(CodeInsightTestFixtureImpl.instantiateAndRun(x, xEditor, new int[0], false), HighlightSeverity.WARNING);
1786 info = ContainerUtil.find(xInfos, xInfo -> xInfo.getDescription().equals("Method 'ffffffffffffff()' is never used"));
1787 assertNotNull(xInfos.toString(), info);
1791 public void testErrorDisappearsRightAfterTypingInsideVisibleAreaWhileDaemonContinuesToChugAlong() throws Throwable {
1792 String text = "class X{\nint xxx;\n{\nint i = <selection>null</selection><caret>;\n" + StringUtil.repeat("{ this.hashCode(); }\n\n\n", 10000) + "}}";
1793 configureByText(StdFileTypes.JAVA, text);
1795 ((EditorImpl)myEditor).getScrollPane().getViewport().setSize(100, 100);
1796 DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true);
1798 myEditor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
1799 ((EditorImpl)myEditor).getScrollPane().getViewport().setViewPosition(new Point(0, 0));
1800 ((EditorImpl)myEditor).getScrollPane().getViewport().setExtentSize(new Dimension(100, 100000));
1801 ProperTextRange visibleRange = VisibleHighlightingPassFactory.calculateVisibleRange(getEditor());
1802 assertTrue(visibleRange.getLength() > 0);
1803 final Document document = myEditor.getDocument();
1804 assertTrue(visibleRange.getLength() < document.getTextLength());
1806 List<HighlightInfo> err1 = highlightErrors();
1807 HighlightInfo info = assertOneElement(err1);
1808 final String errorDescription = "Incompatible types. Found: 'null', required: 'int'";
1809 assertEquals(errorDescription, info.getDescription());
1811 MarkupModelEx model = (MarkupModelEx)DocumentMarkupModel.forDocument(document, myProject, false);
1812 final boolean[] errorRemoved = {false};
1814 model.addMarkupModelListener(getTestRootDisposable(), new MarkupModelListener.Adapter() {
1816 public void beforeRemoved(@NotNull RangeHighlighterEx highlighter) {
1817 Object tt = highlighter.getErrorStripeTooltip();
1818 if (!(tt instanceof HighlightInfo)) return;
1819 String description = ((HighlightInfo)tt).getDescription();
1820 if (errorDescription.equals(description)) {
1821 errorRemoved[0] = true;
1823 List<TextEditorHighlightingPass> passes = myDaemonCodeAnalyzer.getPassesToShowProgressFor(document);
1824 GeneralHighlightingPass ghp = null;
1825 for (TextEditorHighlightingPass pass : passes) {
1826 if (pass instanceof GeneralHighlightingPass && pass.getId() == Pass.UPDATE_ALL) {
1827 assert ghp == null : ghp;
1828 ghp = (GeneralHighlightingPass)pass;
1832 boolean finished = ghp.isFinished();
1833 assertFalse(finished);
1839 List<HighlightInfo> errors = highlightErrors();
1840 assertEmpty(errors);
1841 assertTrue(errorRemoved[0]);
1844 public void testDaemonWorksForDefaultProjectSinceItIsNeededInSettingsDialogForSomeReason() {
1845 assertNotNull(DaemonCodeAnalyzer.getInstance(ProjectManager.getInstance().getDefaultProject()));
1848 public void testChangeEventsAreNotAlwaysGeneric() throws Exception {
1849 String body = "class X {\n" +
1850 "<caret> @org.PPP\n" +
1851 " void dtoArrayDouble() {\n" +
1854 configureByText(JavaFileType.INSTANCE, body);
1855 makeEditorWindowVisible(new Point());
1857 List<HighlightInfo> errors = highlightErrors();
1858 assertFalse(errors.isEmpty());
1861 errors = highlightErrors();
1862 assertEmpty(errors);
1866 errors = highlightErrors();
1867 assertFalse(errors.isEmpty());
1870 public void testInterruptOnTyping() throws Throwable {
1871 @NonNls String filePath = "/psi/resolve/Thinlet.java";
1872 configureByFile(filePath);
1875 final DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
1876 codeAnalyzer.restart();
1877 Runnable interrupt = () -> type(' ');
1879 PsiDocumentManager.getInstance(myProject).commitAllDocuments();
1881 PsiFile file = getFile();
1882 Editor editor = getEditor();
1883 Project project = file.getProject();
1884 CodeInsightTestFixtureImpl.ensureIndexesUpToDate(project);
1885 TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(editor);
1886 codeAnalyzer.runPasses(file, editor.getDocument(), textEditor, ArrayUtil.EMPTY_INT_ARRAY, true, interrupt);
1888 catch (ProcessCanceledException ignored) {
1891 fail("PCE must have been thrown");
1894 public void testAllPassesFinishAfterInterruptOnTyping_Performance() throws Throwable {
1895 @NonNls String filePath = "/psi/resolve/Thinlet.java";
1896 configureByFile(filePath);
1899 final DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
1900 Runnable interrupt = () -> type(' ');
1902 for (int i=0; i<100; i++) {
1904 codeAnalyzer.restart();
1906 PsiDocumentManager.getInstance(myProject).commitAllDocuments();
1908 PsiFile file = getFile();
1909 Editor editor = getEditor();
1910 Project project = file.getProject();
1911 CodeInsightTestFixtureImpl.ensureIndexesUpToDate(project);
1912 TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(editor);
1913 codeAnalyzer.runPasses(file, editor.getDocument(), textEditor, ArrayUtil.EMPTY_INT_ARRAY, true, interrupt);
1915 catch (ProcessCanceledException ignored) {
1916 codeAnalyzer.waitForTermination();
1919 fail("PCE must have been thrown");
1923 public void testCodeFoldingInSplittedWindowAppliesToAllEditors() throws Exception {
1924 final Set<Editor> applied = new THashSet<>();
1925 final Set<Editor> collected = new THashSet<>();
1926 registerFakePass(applied, collected);
1928 configureByText(PlainTextFileType.INSTANCE, "");
1929 Editor editor1 = getEditor();
1930 final Editor editor2 = EditorFactory.getInstance().createEditor(editor1.getDocument(),getProject());
1931 Disposer.register(getProject(), () -> EditorFactory.getInstance().releaseEditor(editor2));
1932 TextEditor textEditor1 = new PsiAwareTextEditorProvider().getTextEditor(editor1);
1933 TextEditor textEditor2 = new PsiAwareTextEditorProvider().getTextEditor(editor2);
1935 List<HighlightInfo> errors = myDaemonCodeAnalyzer.runPasses(myFile, editor1.getDocument(), Arrays.asList(textEditor1,textEditor2), new int[0], false, null);
1936 assertEmpty(errors);
1938 assertEquals(collected, ContainerUtil.newHashSet(editor1, editor2));
1939 assertEquals(applied, ContainerUtil.newHashSet(editor1, editor2));
1942 private void registerFakePass(@NotNull final Set<Editor> applied, @NotNull final Set<Editor> collected) {
1943 class Fac extends AbstractProjectComponent implements TextEditorHighlightingPassFactory {
1944 private Fac(Project project) {
1949 public TextEditorHighlightingPass createHighlightingPass(@NotNull PsiFile file, @NotNull final Editor editor) {
1950 return new EditorBoundHighlightingPass(editor, file, false) {
1952 public void doCollectInformation(@NotNull ProgressIndicator progress) {
1953 collected.add(editor);
1957 public void doApplyInformationToEditor() {
1958 applied.add(editor);
1963 TextEditorHighlightingPassRegistrar registrar = TextEditorHighlightingPassRegistrar.getInstance(getProject());
1964 registrar.registerTextEditorHighlightingPass(new Fac(getProject()), null, null, false, -1);
1967 private volatile boolean runHeavyProcessing;
1968 public void testDaemonDisablesItselfDuringHeavyProcessing() throws Exception {
1969 runHeavyProcessing = false;
1970 DaemonCodeAnalyzerSettings settings = DaemonCodeAnalyzerSettings.getInstance();
1971 int delay = settings.AUTOREPARSE_DELAY;
1972 settings.AUTOREPARSE_DELAY = 0;
1974 final Set<Editor> applied = Collections.synchronizedSet(new THashSet<>());
1975 final Set<Editor> collected = Collections.synchronizedSet(new THashSet<>());
1976 registerFakePass(applied, collected);
1978 configureByText(PlainTextFileType.INSTANCE, "");
1979 Editor editor = getEditor();
1980 EditorTracker editorTracker = getProject().getComponent(EditorTracker.class);
1981 editorTracker.setActiveEditors(Collections.singletonList(editor));
1982 while (HeavyProcessLatch.INSTANCE.isRunning()) {
1983 UIUtil.dispatchAllInvocationEvents();
1985 type("xxx"); // restart daemon
1986 assertTrue(editorTracker.getActiveEditors().contains(editor));
1987 assertSame(editor, FileEditorManager.getInstance(myProject).getSelectedTextEditor());
1990 // wait for first pass to complete
1991 long start = System.currentTimeMillis();
1992 while (myDaemonCodeAnalyzer.isRunning() || !applied.contains(editor)) {
1993 UIUtil.dispatchAllInvocationEvents();
1994 if (System.currentTimeMillis() - start > 10000) {
1995 fail("Too long waiting for daemon");
1999 runHeavyProcessing = true;
2000 ApplicationManager.getApplication().executeOnPooledThread(() -> {
2001 AccessToken token = HeavyProcessLatch.INSTANCE.processStarted("my own heavy op");
2003 while (runHeavyProcessing) {
2010 while (!HeavyProcessLatch.INSTANCE.isRunning()) {
2011 UIUtil.dispatchAllInvocationEvents();
2016 type("xxx"); // try to restart daemon
2018 start = System.currentTimeMillis();
2019 while (System.currentTimeMillis() < start + 5000) {
2020 assertEmpty(applied); // it should not restart
2021 assertEmpty(collected);
2022 UIUtil.dispatchAllInvocationEvents();
2026 runHeavyProcessing = false;
2027 settings.AUTOREPARSE_DELAY = delay;
2032 public void testModificationInsideCodeBlockDoesNotRehighlightWholeFile() throws Exception {
2033 configureByText(JavaFileType.INSTANCE, "class X { int f = \"error\"; int f() { int gg<caret> = 11; return 0;} }");
2034 List<HighlightInfo> errors = highlightErrors();
2035 assertEquals(1, errors.size());
2036 assertEquals("Incompatible types. Found: 'java.lang.String', required: 'int'", errors.get(0).getDescription());
2038 errors.get(0).highlighter.dispose();
2040 errors = highlightErrors();
2041 assertEmpty(errors);
2044 errors = highlightErrors();
2045 assertEmpty(errors);
2047 myEditor.getCaretModel().moveToOffset(0);
2049 errors = highlightErrors();
2050 assertEquals(1, errors.size());
2051 assertEquals("Incompatible types. Found: 'java.lang.String', required: 'int'", errors.get(0).getDescription());
2054 public void _testCaretMovementDoesNotRestartHighlighting() throws Exception {
2055 configureByText(JavaFileType.INSTANCE, "class X { int f = \"error\"; int f() { int gg<caret> = 11; return 0;} }");
2057 TextEditor textEditor = TextEditorProvider.getInstance().getTextEditor(getEditor());
2058 final DaemonCodeAnalyzerImpl di = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
2059 final AtomicReference<ProgressIndicator> indicator = new AtomicReference<>();
2060 final List<HighlightInfo> errors = filter(
2061 di.runPasses(getFile(), getEditor().getDocument(), textEditor, ArrayUtil.EMPTY_INT_ARRAY, false, () -> {
2062 if (indicator.get() == null) {
2063 indicator.set(di.getUpdateProgress());
2065 assertSame(indicator.get(), di.getUpdateProgress());
2067 if (getEditor().getCaretModel().getOffset() == getEditor().getDocument().getTextLength()-1) {
2068 getEditor().getCaretModel().moveToOffset(0);
2070 }), HighlightSeverity.ERROR);
2072 assertEquals(1, errors.size());
2073 assertEquals("Incompatible types. Found: 'java.lang.String', required: 'int'", errors.get(0).getDescription());
2077 public void testHighlightingDoesWaitForEmbarrassinglySlowExternalAnnotatorsToFinish() throws Exception {
2078 configureByText(JavaFileType.INSTANCE, "class X { int f() { int gg<caret> = 11; return 0;} }");
2079 final AtomicBoolean run = new AtomicBoolean();
2080 final int SLEEP = 20000;
2081 ExternalAnnotator<Integer, Integer> annotator = new ExternalAnnotator<Integer, Integer>() {
2084 public Integer collectInformation(@NotNull PsiFile file) {
2090 public Integer doAnnotate(final Integer collectedInfo) {
2091 TimeoutUtil.sleep(SLEEP);
2096 public void apply(@NotNull final PsiFile file, final Integer annotationResult, @NotNull final AnnotationHolder holder) {
2100 ExternalLanguageAnnotators.INSTANCE.addExplicitExtension(JavaLanguage.INSTANCE, annotator);
2103 long start = System.currentTimeMillis();
2104 List<HighlightInfo> errors = filter(CodeInsightTestFixtureImpl.instantiateAndRun(getFile(), getEditor(), new int[0], false),
2105 HighlightSeverity.ERROR);
2106 long elapsed = System.currentTimeMillis() - start;
2108 assertEquals(0, errors.size());
2109 assertTrue("Elapsed: "+elapsed, elapsed >= SLEEP);
2110 assertTrue(run.get());
2113 ExternalLanguageAnnotators.INSTANCE.removeExplicitExtension(JavaLanguage.INSTANCE, annotator);
2117 public void testModificationInExcludedFileDoesNotCauseRehighlight() throws Exception {
2118 final PsiFile excluded = configureByText(JavaFileType.INSTANCE, "class EEE { void f(){} }");
2119 PsiTestUtil.addExcludedRoot(myModule, excluded.getVirtualFile().getParent());
2121 configureByText(JavaFileType.INSTANCE, "class X { <caret> }");
2122 List<HighlightInfo> errors = highlightErrors();
2123 assertEmpty(errors);
2124 FileStatusMap me = DaemonCodeAnalyzerEx.getInstanceEx(getProject()).getFileStatusMap();
2125 TextRange scope = me.getFileDirtyScope(getEditor().getDocument(), Pass.UPDATE_ALL);
2128 WriteCommandAction.runWriteCommandAction(getProject(), () -> ((PsiJavaFile)excluded).getClasses()[0].getMethods()[0].delete());
2130 UIUtil.dispatchAllInvocationEvents();
2131 scope = me.getFileDirtyScope(getEditor().getDocument(), Pass.UPDATE_ALL);
2135 public void testModificationInWorkspaceXmlDoesNotCauseRehighlight() throws Exception {
2136 configureByText(JavaFileType.INSTANCE, "class X { <caret> }");
2137 ApplicationEx application = ApplicationManagerEx.getApplicationEx();
2138 boolean appSave = application.isDoNotSave();
2139 application.doNotSave(false);
2141 application.saveAll();
2142 final PsiFile excluded = PsiManager.getInstance(getProject()).findFile(getProject().getWorkspaceFile());
2144 List<HighlightInfo> errors = highlightErrors();
2145 assertEmpty(errors);
2146 FileStatusMap me = DaemonCodeAnalyzerEx.getInstanceEx(getProject()).getFileStatusMap();
2147 TextRange scope = me.getFileDirtyScope(getEditor().getDocument(), Pass.UPDATE_ALL);
2150 WriteCommandAction.runWriteCommandAction(getProject(), () -> {
2151 Document document = PsiDocumentManager.getInstance(getProject()).getDocument(excluded);
2152 document.insertString(0, "<!-- dsfsd -->");
2153 PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
2155 UIUtil.dispatchAllInvocationEvents();
2156 scope = me.getFileDirtyScope(getEditor().getDocument(), Pass.UPDATE_ALL);
2160 application.doNotSave(appSave);
2164 public void testLightBulbDoesNotUpdateIntentionsInEDT() throws Exception {
2165 final IntentionAction longLongUpdate = new AbstractIntentionAction() {
2167 public void invoke(@NotNull Project project, Editor editor, PsiFile file) {
2173 public String getText() {
2174 return "LongAction";
2178 public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
2179 if (ApplicationManager.getApplication().isDispatchThread()) {
2180 throw new RuntimeException("Must not update actions in EDT");
2185 IntentionManager.getInstance().addAction(longLongUpdate);
2186 Disposer.register(myTestRootDisposable, () -> IntentionManager.getInstance().unregisterIntention(longLongUpdate));
2187 configureByText(JavaFileType.INSTANCE, "class X { <caret> }");
2188 makeEditorWindowVisible(new Point(0, 0));
2190 myDaemonCodeAnalyzer.restart();
2191 DaemonCodeAnalyzerSettings mySettings = DaemonCodeAnalyzerSettings.getInstance();
2192 mySettings.AUTOREPARSE_DELAY = 0;
2193 for (int i=0; i<1000; i++) {
2195 UIUtil.dispatchAllInvocationEvents();
2197 DaemonProgressIndicator updateProgress = myDaemonCodeAnalyzer.getUpdateProgress();
2198 while(myDaemonCodeAnalyzer.getUpdateProgress() == updateProgress) { // wait until daemon started
2199 UIUtil.dispatchAllInvocationEvents();
2201 long start = System.currentTimeMillis();
2202 while (myDaemonCodeAnalyzer.isRunning() && System.currentTimeMillis() < start + 500) {
2203 UIUtil.dispatchAllInvocationEvents(); // wait for a bit more until ShowIntentionsPass.doApplyInformationToEditor() called
2208 public void testLightBulbIsHiddenWhenFixRangeIsCollapsed() {
2209 configureByText(StdFileTypes.JAVA, "class S { void foo() { boolean var; if (<selection>va<caret>r</selection>) {}} }");
2210 ((EditorImpl)myEditor).getScrollPane().getViewport().setSize(1000, 1000);
2212 final Set<LightweightHint> visibleHints = ContainerUtil.newIdentityTroveSet();
2213 getProject().getMessageBus().connect(myTestRootDisposable).subscribe(EditorHintListener.TOPIC, new EditorHintListener() {
2215 public void hintShown(final Project project, final LightweightHint hint, final int flags) {
2216 visibleHints.add(hint);
2217 hint.addHintListener(new HintListener() {
2219 public void hintHidden(EventObject event) {
2220 visibleHints.remove(hint);
2221 hint.removeHintListener(this);
2228 IntentionHintComponent lastHintBeforeDeletion = myDaemonCodeAnalyzer.getLastIntentionHint();
2229 assertNotNull(lastHintBeforeDeletion);
2233 IntentionHintComponent lastHintAfterDeletion = myDaemonCodeAnalyzer.getLastIntentionHint();
2234 assertSame(lastHintBeforeDeletion, lastHintAfterDeletion);
2236 assertEmpty(visibleHints);
2239 public void testCodeFoldingPassRestartsOnRegionUnfolding() throws Exception {
2240 DaemonCodeAnalyzerSettings settings = DaemonCodeAnalyzerSettings.getInstance();
2241 int savedDelay = settings.AUTOREPARSE_DELAY;
2242 settings.AUTOREPARSE_DELAY = 0;
2244 configureByText(StdFileTypes.JAVA, "class Foo {\n" +
2249 CodeFoldingManager.getInstance(getProject()).buildInitialFoldings(myEditor);
2251 EditorTestUtil.executeAction(myEditor, IdeActions.ACTION_COLLAPSE_ALL_REGIONS);
2253 checkFoldingState("[FoldRegion +(25:33), placeholder='{...}']");
2255 new WriteCommandAction<Void>(myProject){
2257 protected void run(@NotNull Result<Void> result) throws Throwable {
2258 myEditor.getDocument().insertString(0, "/*");
2262 checkFoldingState("[FoldRegion -(0:37), placeholder='/.../', FoldRegion +(27:35), placeholder='{...}']");
2264 EditorTestUtil.executeAction(myEditor, IdeActions.ACTION_EXPAND_ALL_REGIONS);
2266 checkFoldingState("[FoldRegion -(0:37), placeholder='/.../']");
2269 settings.AUTOREPARSE_DELAY = savedDelay;
2273 private void checkFoldingState(String expected) {
2274 assertEquals(expected, Arrays.toString(myEditor.getFoldingModel().getAllFoldRegions()));
2277 private void waitForDaemon() {
2278 long deadline = System.currentTimeMillis() + 60_000;
2279 while (!daemonIsWorkingOrPending()) {
2280 if (System.currentTimeMillis() > deadline) fail("Too long waiting for daemon to start");
2281 UIUtil.dispatchInvocationEvent();
2283 while (daemonIsWorkingOrPending()) {
2284 if (System.currentTimeMillis() > deadline) fail("Too long waiting for daemon to finish");
2285 UIUtil.dispatchInvocationEvent();
2289 private boolean daemonIsWorkingOrPending() {
2290 return PsiDocumentManager.getInstance(myProject).isUncommited(myEditor.getDocument()) || myDaemonCodeAnalyzer.isRunningOrPending();
2293 public void testRehighlightInDebuggerExpressionFragment() throws Exception {
2294 PsiExpressionCodeFragment fragment = JavaCodeFragmentFactory.getInstance(getProject()).createExpressionCodeFragment("+ <caret>\"a\"", null,
2295 PsiType.getJavaLangObject(getPsiManager(), GlobalSearchScope.allScope(getProject())), true);
2297 Document document = PsiDocumentManager.getInstance(getProject()).getDocument(fragment);
2298 myEditor = EditorFactory.getInstance().createEditor(document, getProject(), StdFileTypes.JAVA, false);
2300 ProperTextRange visibleRange = makeEditorWindowVisible(new Point(0, 0));
2301 assertEquals(document.getTextLength(), visibleRange.getLength());
2304 final EditorInfo editorInfo = new EditorInfo(document.getText());
2306 final String newFileText = editorInfo.getNewFileText();
2307 ApplicationManager.getApplication().runWriteAction(() -> {
2308 if (!document.getText().equals(newFileText)) {
2309 document.setText(newFileText);
2312 editorInfo.applyToEditor(myEditor);
2315 PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
2318 List<HighlightInfo> errors = highlightErrors();
2319 HighlightInfo error = assertOneElement(errors);
2320 assertEquals("Operator '+' cannot be applied to 'java.lang.String'", error.getDescription());
2324 Collection<HighlightInfo> afterTyping = highlightErrors();
2325 HighlightInfo after = assertOneElement(afterTyping);
2326 assertEquals("Operator '+' cannot be applied to 'java.lang.String'", after.getDescription());
2329 EditorFactory.getInstance().releaseEditor(myEditor);
2333 public void testFileReload() throws Exception {
2334 VirtualFile file = createFile("a.java", "").getVirtualFile();
2335 Document document = getDocument(file);
2336 assertNotNull(document);
2338 FileStatusMap fileStatusMap = myDaemonCodeAnalyzer.getFileStatusMap();
2340 WriteCommandAction.runWriteCommandAction(getProject(), () -> {
2341 PlatformTestUtil.tryGcSoftlyReachableObjects();
2342 assertNull(PsiDocumentManager.getInstance(getProject()).getCachedPsiFile(document));
2344 document.insertString(0, "class X { void foo() {}}");
2345 assertEquals(TextRange.from(0, document.getTextLength()), fileStatusMap.getFileDirtyScope(document, Pass.UPDATE_ALL));
2347 FileContentUtilCore.reparseFiles(file);
2348 assertEquals(TextRange.from(0, document.getTextLength()), fileStatusMap.getFileDirtyScope(document, Pass.UPDATE_ALL));
2350 findClass("X").getMethods()[0].delete();
2351 assertEquals(TextRange.from(0, document.getTextLength()), fileStatusMap.getFileDirtyScope(document, Pass.UPDATE_ALL));