diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/DaemonInspectionsRespondToChangesTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/DaemonInspectionsRespondToChangesTest.java index 1c167a5847bc..1eb9e25cfb9d 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/DaemonInspectionsRespondToChangesTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/DaemonInspectionsRespondToChangesTest.java @@ -28,6 +28,7 @@ import com.intellij.openapi.editor.impl.EditorImpl; import com.intellij.openapi.editor.markup.MarkupModel; import com.intellij.openapi.editor.markup.RangeHighlighter; import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.TextEditor; import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider; import com.intellij.openapi.fileTypes.PlainTextFileType; @@ -636,7 +637,7 @@ public class DaemonInspectionsRespondToChangesTest extends DaemonAnalyzerTestCas assertNull(expectedVisibleRange); // check the inspection was run } - // add file-level "blah" if there are identifiers containing "XXX" + // add file-level "blah" for each identifier containing "XXX" private static class MyFileLevelInspection extends MyInspectionBase { @NotNull @Override @@ -670,6 +671,40 @@ public class DaemonInspectionsRespondToChangesTest extends DaemonAnalyzerTestCas assertOneElement(myDaemonCodeAnalyzer.getFileLevelHighlights(getProject(), getFile())); } + public void testFileLevelHighlightingDoesNotDuplicateOnTypingOrOnFileCloseReopen() { + registerInspection(new MyFileLevelInspection()); + @Language("JAVA") + String text = """ + class X { + int XXX; + }"""; + PsiFile psiFile = configureByText(JavaFileType.INSTANCE, text); + + assertEmpty(highlightErrors()); + assertOneElement(myDaemonCodeAnalyzer.getFileLevelHighlights(getProject(), getFile())); + FileEditorManager.getInstance(myProject).closeFile(psiFile.getVirtualFile()); + + for (int i=0; i<100; i++) { + configureByExistingFile(psiFile.getVirtualFile()); + getEditor().getCaretModel().moveToOffset(getEditor().getDocument().getText().indexOf("XXX") + "XXX".length()); + + UIUtil.dispatchAllInvocationEvents(); + type('2'); + assertEmpty(highlightErrors()); + UIUtil.dispatchAllInvocationEvents(); + assertOneElement(myDaemonCodeAnalyzer.getFileLevelHighlights(getProject(), getFile())); + UIUtil.dispatchAllInvocationEvents(); + backspace(); + assertEmpty(highlightErrors()); + UIUtil.dispatchAllInvocationEvents(); + assertOneElement(myDaemonCodeAnalyzer.getFileLevelHighlights(getProject(), getFile())); + + LOG.debug("i = " + i); + + FileEditorManager.getInstance(myProject).closeFile(psiFile.getVirtualFile()); + } + } + public void testFileLevelWithEverChangingDescriptionMustUpdateOnTyping() { // add file-level "blah" if there are identifiers containing "xxx" class XXXIdentifierFileLevelInspection extends MyInspectionBase { diff --git a/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/HighlightInfoUpdaterImpl.java b/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/HighlightInfoUpdaterImpl.java index 7b5df6e74331..a8e02f70bef3 100644 --- a/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/HighlightInfoUpdaterImpl.java +++ b/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/HighlightInfoUpdaterImpl.java @@ -244,10 +244,10 @@ final class HighlightInfoUpdaterImpl extends HighlightInfoUpdater implements Dis } // disposes highlighter, and schedules removal from the file-level component if this highlighter happened to be file-level - private static void disposeWithFileLevel(@NotNull HighlightInfo info, - @NotNull RangeHighlighterEx highlighter, - @NotNull HighlightingSession highlightingSession) { - if (info.isFileLevelAnnotation()) { + static void disposeWithFileLevel(@Nullable HighlightInfo info, + @NotNull RangeHighlighterEx highlighter, + @NotNull HighlightingSession highlightingSession) { + if (info != null && info.isFileLevelAnnotation()) { ((HighlightingSessionImpl)highlightingSession).removeFileLevelHighlight(info); } highlighter.dispose(); @@ -357,7 +357,7 @@ final class HighlightInfoUpdaterImpl extends HighlightInfoUpdater implements Dis RangeHighlighterEx salvagedHighlighter = toReuse.pickupHighlighterFromGarbageBin(0, psiFile.getTextLength(), -409423948); HighlightInfo oldFileInfo = salvagedHighlighter == null ? null : HighlightInfo.fromRangeHighlighter(salvagedHighlighter); if (oldFileInfo != null) { - ((HighlightingSessionImpl)session).removeFileLevelHighlight(oldFileInfo); + disposeWithFileLevel(oldFileInfo, salvagedHighlighter, session); salvagedHighlighter = null; } ((HighlightingSessionImpl)session).addFileLevelHighlight(info, salvagedHighlighter); diff --git a/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/UpdateHighlightersUtil.java b/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/UpdateHighlightersUtil.java index 7cf234241f79..0bc68d6bbc92 100644 --- a/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/UpdateHighlightersUtil.java +++ b/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/UpdateHighlightersUtil.java @@ -231,16 +231,13 @@ public final class UpdateHighlightersUtil { // do not remove obsolete highlighters if we are in "essential highlighting only" mode, because otherwise all inspection-produced results would be gone for (RangeHighlighter highlighter : infosToRemove.forAllInGarbageBin()) { boolean shouldRemove = shouldRemoveHighlighter(highlighter, session); - HighlightInfo info = HighlightInfo.fromRangeHighlighter(highlighter); if (LOG.isDebugEnabled()) { LOG.debug("incinerateObsoleteHighlighters "+highlighter+"; shouldRemove:"+shouldRemove); } if (shouldRemove) { - highlighter.dispose(); + HighlightInfo info = HighlightInfo.fromRangeHighlighter(highlighter); + HighlightInfoUpdaterImpl.disposeWithFileLevel(info, (RangeHighlighterEx)highlighter, session); changed = true; - if (info != null && info.isFileLevelAnnotation()) { - ((HighlightingSessionImpl)session).removeFileLevelHighlight(info); - } } } return changed; @@ -458,8 +455,16 @@ public final class UpdateHighlightersUtil { }); for (HighlightInfo info : toRemove) { - if (!info.getHighlighter().isValid() || info.type.equals(HighlightInfoType.WRONG_REF)) { - info.getHighlighter().dispose(); + RangeHighlighterEx highlighter = info.getHighlighter(); + if (!highlighter.isValid() || info.type.equals(HighlightInfoType.WRONG_REF)) { + if (info.isFileLevelAnnotation()) { + DaemonCodeAnalyzerEx codeAnalyzer = DaemonCodeAnalyzerEx.getInstanceEx(project); + PsiFile psiFile = PsiDocumentManager.getInstance(project).getCachedPsiFile(document); + if (psiFile != null) { + codeAnalyzer.removeFileLevelHighlight(psiFile, info); + } + } + highlighter.dispose(); } }