check for sudden dumb mode in on-the-fly import (IDEA-154934)
[idea/community.git] / platform / lang-impl / src / com / intellij / codeInsight / daemon / impl / DefaultHighlightInfoProcessor.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.codeInsight.daemon.impl;
17
18 import com.intellij.codeHighlighting.Pass;
19 import com.intellij.openapi.application.ApplicationManager;
20 import com.intellij.openapi.application.TransactionGuard;
21 import com.intellij.openapi.editor.Document;
22 import com.intellij.openapi.editor.Editor;
23 import com.intellij.openapi.editor.colors.EditorColorsScheme;
24 import com.intellij.openapi.editor.ex.MarkupModelEx;
25 import com.intellij.openapi.editor.impl.DocumentMarkupModel;
26 import com.intellij.openapi.editor.impl.EditorMarkupModelImpl;
27 import com.intellij.openapi.editor.markup.MarkupModel;
28 import com.intellij.openapi.project.DumbService;
29 import com.intellij.openapi.project.Project;
30 import com.intellij.openapi.util.ProperTextRange;
31 import com.intellij.openapi.util.TextRange;
32 import com.intellij.psi.PsiDocumentManager;
33 import com.intellij.psi.PsiFile;
34 import com.intellij.psi.util.PsiUtilBase;
35 import com.intellij.util.Alarm;
36 import com.intellij.util.Processor;
37 import com.intellij.util.ui.UIUtil;
38 import org.jetbrains.annotations.NotNull;
39 import org.jetbrains.annotations.Nullable;
40
41 import java.util.List;
42
43 public class DefaultHighlightInfoProcessor extends HighlightInfoProcessor {
44   @Override
45   public void highlightsInsideVisiblePartAreProduced(@NotNull final HighlightingSession session,
46                                                      @NotNull final List<HighlightInfo> infos,
47                                                      @NotNull TextRange priorityRange,
48                                                      @NotNull TextRange restrictRange,
49                                                      final int groupId) {
50     final PsiFile psiFile = session.getPsiFile();
51     final Project project = psiFile.getProject();
52     final Document document = PsiDocumentManager.getInstance(project).getDocument(psiFile);
53     if (document == null) return;
54     final long modificationStamp = document.getModificationStamp();
55     final TextRange priorityIntersection = priorityRange.intersection(restrictRange);
56
57     final Editor editor = session.getEditor();
58     TransactionGuard.submitTransaction(project, new Runnable() {
59       @Override
60       public void run() {
61         if (modificationStamp != document.getModificationStamp()) return;
62         if (priorityIntersection != null) {
63           MarkupModel markupModel = DocumentMarkupModel.forDocument(document, project, true);
64
65           EditorColorsScheme scheme = session.getColorsScheme();
66           UpdateHighlightersUtil.setHighlightersInRange(project, document, priorityIntersection, scheme, infos,
67                                                         (MarkupModelEx)markupModel, groupId);
68         }
69         if (editor != null && !editor.isDisposed()) {
70           // usability: show auto import popup as soon as possible
71           if (!DumbService.isDumb(project)) {
72             new ShowAutoImportPass(project, psiFile, editor).addImports();
73           }
74
75           DaemonListeners.repaintErrorStripeRenderer(editor, project);
76         }
77       }
78     });
79   }
80
81   @Override
82   public void highlightsOutsideVisiblePartAreProduced(@NotNull final HighlightingSession session,
83                                                       @NotNull final List<HighlightInfo> infos,
84                                                       @NotNull final TextRange priorityRange,
85                                                       @NotNull final TextRange restrictedRange, final int groupId) {
86     final PsiFile psiFile = session.getPsiFile();
87     final Project project = psiFile.getProject();
88     final Document document = PsiDocumentManager.getInstance(project).getDocument(psiFile);
89     if (document == null) return;
90     final long modificationStamp = document.getModificationStamp();
91     UIUtil.invokeLaterIfNeeded(new Runnable() {
92       @Override
93       public void run() {
94         if (project.isDisposed() || modificationStamp != document.getModificationStamp()) return;
95
96         EditorColorsScheme scheme = session.getColorsScheme();
97
98         UpdateHighlightersUtil.setHighlightersOutsideRange(project, document, psiFile, infos, scheme,
99                                                            restrictedRange.getStartOffset(), restrictedRange.getEndOffset(),
100                                                            ProperTextRange.create(priorityRange),
101                                                            groupId);
102         Editor editor = session.getEditor();
103         if (editor != null) {
104           DaemonListeners.repaintErrorStripeRenderer(editor, project);
105         }
106       }
107     });
108
109   }
110
111   @Override
112   public void allHighlightsForRangeAreProduced(@NotNull HighlightingSession session,
113                                                @NotNull TextRange elementRange,
114                                                @Nullable List<HighlightInfo> infos) {
115     PsiFile psiFile = session.getPsiFile();
116     killAbandonedHighlightsUnder(psiFile, elementRange, infos, session);
117   }
118
119   private static void killAbandonedHighlightsUnder(@NotNull PsiFile psiFile,
120                                                    @NotNull final TextRange range,
121                                                    @Nullable final List<HighlightInfo> infos,
122                                                    @NotNull final HighlightingSession highlightingSession) {
123     final Project project = psiFile.getProject();
124     final Document document = PsiDocumentManager.getInstance(project).getDocument(psiFile);
125     if (document == null) return;
126     DaemonCodeAnalyzerEx
127       .processHighlights(document, project, null, range.getStartOffset(), range.getEndOffset(), new Processor<HighlightInfo>() {
128         @Override
129         public boolean process(final HighlightInfo existing) {
130           if (existing.isBijective() &&
131               existing.getGroup() == Pass.UPDATE_ALL &&
132               range.equalsToRange(existing.getActualStartOffset(), existing.getActualEndOffset())) {
133             if (infos != null) {
134               for (HighlightInfo created : infos) {
135                 if (existing.equalsByActualOffset(created)) return true;
136               }
137             }
138             // seems that highlight info "existing" is going to disappear
139             // remove it earlier
140             ((HighlightingSessionImpl)highlightingSession).queueDisposeHighlighter(existing.highlighter);
141           }
142           return true;
143         }
144       });
145   }
146
147   @Override
148   public void infoIsAvailable(@NotNull HighlightingSession session,
149                               @NotNull HighlightInfo info,
150                               @NotNull TextRange priorityRange,
151                               @NotNull TextRange restrictedRange,
152                               int groupId) {
153     HighlightingSessionImpl impl = (HighlightingSessionImpl)session;
154     impl.queueHighlightInfo(info, priorityRange, restrictedRange, groupId);
155   }
156
157   @Override
158   public void progressIsAdvanced(@NotNull HighlightingSession highlightingSession, double progress) {
159     PsiFile file = highlightingSession.getPsiFile();
160     Editor editor = highlightingSession.getEditor();
161     repaintTrafficIcon(file, editor, progress);
162   }
163
164   private final Alarm repaintIconAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
165   private void repaintTrafficIcon(@NotNull final PsiFile file, final Editor editor, double progress) {
166     if (ApplicationManager.getApplication().isCommandLine()) return;
167
168     if (repaintIconAlarm.isEmpty() || progress >= 1) {
169       repaintIconAlarm.addRequest(new Runnable() {
170         @Override
171         public void run() {
172           Project myProject = file.getProject();
173           if (myProject.isDisposed()) return;
174           Editor myeditor = editor;
175           if (myeditor == null) {
176             myeditor = PsiUtilBase.findEditor(file);
177           }
178           if (myeditor == null || myeditor.isDisposed()) return;
179           EditorMarkupModelImpl markup = (EditorMarkupModelImpl)myeditor.getMarkupModel();
180           markup.repaintTrafficLightIcon();
181           DaemonListeners.repaintErrorStripeRenderer(myeditor, myProject);
182         }
183       }, 50, null);
184     }
185   }
186 }