0c3b88741e06d9877f53c2fe5c7b86db1d132d41
[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.Project;
29 import com.intellij.openapi.util.ProperTextRange;
30 import com.intellij.openapi.util.TextRange;
31 import com.intellij.psi.PsiDocumentManager;
32 import com.intellij.psi.PsiFile;
33 import com.intellij.psi.util.PsiUtilBase;
34 import com.intellij.util.Alarm;
35 import com.intellij.util.Processor;
36 import com.intellij.util.ui.UIUtil;
37 import org.jetbrains.annotations.NotNull;
38 import org.jetbrains.annotations.Nullable;
39
40 import java.util.List;
41
42 public class DefaultHighlightInfoProcessor extends HighlightInfoProcessor {
43   @Override
44   public void highlightsInsideVisiblePartAreProduced(@NotNull final HighlightingSession session,
45                                                      @NotNull final List<HighlightInfo> infos,
46                                                      @NotNull TextRange priorityRange,
47                                                      @NotNull TextRange restrictRange,
48                                                      final int groupId) {
49     final PsiFile psiFile = session.getPsiFile();
50     final Project project = psiFile.getProject();
51     final Document document = PsiDocumentManager.getInstance(project).getDocument(psiFile);
52     if (document == null) return;
53     final long modificationStamp = document.getModificationStamp();
54     final TextRange priorityIntersection = priorityRange.intersection(restrictRange);
55
56     final Editor editor = session.getEditor();
57     TransactionGuard.submitTransaction(project, new Runnable() {
58       @Override
59       public void run() {
60         if (modificationStamp != document.getModificationStamp()) return;
61         if (priorityIntersection != null) {
62           MarkupModel markupModel = DocumentMarkupModel.forDocument(document, project, true);
63
64           EditorColorsScheme scheme = session.getColorsScheme();
65           UpdateHighlightersUtil.setHighlightersInRange(project, document, priorityIntersection, scheme, infos,
66                                                         (MarkupModelEx)markupModel, groupId);
67         }
68         if (editor != null && !editor.isDisposed()) {
69           // usability: show auto import popup as soon as possible
70           new ShowAutoImportPass(project, psiFile, editor).addImports();
71           
72           DaemonListeners.repaintErrorStripeRenderer(editor, project);
73         }
74       }
75     });
76   }
77
78   @Override
79   public void highlightsOutsideVisiblePartAreProduced(@NotNull final HighlightingSession session,
80                                                       @NotNull final List<HighlightInfo> infos,
81                                                       @NotNull final TextRange priorityRange,
82                                                       @NotNull final TextRange restrictedRange, final int groupId) {
83     final PsiFile psiFile = session.getPsiFile();
84     final Project project = psiFile.getProject();
85     final Document document = PsiDocumentManager.getInstance(project).getDocument(psiFile);
86     if (document == null) return;
87     final long modificationStamp = document.getModificationStamp();
88     UIUtil.invokeLaterIfNeeded(new Runnable() {
89       @Override
90       public void run() {
91         if (project.isDisposed() || modificationStamp != document.getModificationStamp()) return;
92
93         EditorColorsScheme scheme = session.getColorsScheme();
94
95         UpdateHighlightersUtil.setHighlightersOutsideRange(project, document, psiFile, infos, scheme,
96                                                            restrictedRange.getStartOffset(), restrictedRange.getEndOffset(),
97                                                            ProperTextRange.create(priorityRange),
98                                                            groupId);
99         Editor editor = session.getEditor();
100         if (editor != null) {
101           DaemonListeners.repaintErrorStripeRenderer(editor, project);
102         }
103       }
104     });
105
106   }
107
108   @Override
109   public void allHighlightsForRangeAreProduced(@NotNull HighlightingSession session,
110                                                @NotNull TextRange elementRange,
111                                                @Nullable List<HighlightInfo> infos) {
112     PsiFile psiFile = session.getPsiFile();
113     killAbandonedHighlightsUnder(psiFile, elementRange, infos, session);
114   }
115
116   private static void killAbandonedHighlightsUnder(@NotNull PsiFile psiFile,
117                                                    @NotNull final TextRange range,
118                                                    @Nullable final List<HighlightInfo> infos,
119                                                    @NotNull final HighlightingSession highlightingSession) {
120     final Project project = psiFile.getProject();
121     final Document document = PsiDocumentManager.getInstance(project).getDocument(psiFile);
122     if (document == null) return;
123     DaemonCodeAnalyzerEx
124       .processHighlights(document, project, null, range.getStartOffset(), range.getEndOffset(), new Processor<HighlightInfo>() {
125         @Override
126         public boolean process(final HighlightInfo existing) {
127           if (existing.isBijective() &&
128               existing.getGroup() == Pass.UPDATE_ALL &&
129               range.equalsToRange(existing.getActualStartOffset(), existing.getActualEndOffset())) {
130             if (infos != null) {
131               for (HighlightInfo created : infos) {
132                 if (existing.equalsByActualOffset(created)) return true;
133               }
134             }
135             // seems that highlight info "existing" is going to disappear
136             // remove it earlier
137             ((HighlightingSessionImpl)highlightingSession).queueDisposeHighlighter(existing.highlighter);
138           }
139           return true;
140         }
141       });
142   }
143
144   @Override
145   public void infoIsAvailable(@NotNull HighlightingSession session,
146                               @NotNull HighlightInfo info,
147                               @NotNull TextRange priorityRange,
148                               @NotNull TextRange restrictedRange,
149                               int groupId) {
150     HighlightingSessionImpl impl = (HighlightingSessionImpl)session;
151     impl.queueHighlightInfo(info, priorityRange, restrictedRange, groupId);
152   }
153
154   @Override
155   public void progressIsAdvanced(@NotNull HighlightingSession highlightingSession, double progress) {
156     PsiFile file = highlightingSession.getPsiFile();
157     Editor editor = highlightingSession.getEditor();
158     repaintTrafficIcon(file, editor, progress);
159   }
160
161   private final Alarm repaintIconAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
162   private void repaintTrafficIcon(@NotNull final PsiFile file, final Editor editor, double progress) {
163     if (ApplicationManager.getApplication().isCommandLine()) return;
164
165     if (repaintIconAlarm.isEmpty() || progress >= 1) {
166       repaintIconAlarm.addRequest(new Runnable() {
167         @Override
168         public void run() {
169           Project myProject = file.getProject();
170           if (myProject.isDisposed()) return;
171           Editor myeditor = editor;
172           if (myeditor == null) {
173             myeditor = PsiUtilBase.findEditor(file);
174           }
175           if (myeditor == null || myeditor.isDisposed()) return;
176           EditorMarkupModelImpl markup = (EditorMarkupModelImpl)myeditor.getMarkupModel();
177           markup.repaintTrafficLightIcon();
178           DaemonListeners.repaintErrorStripeRenderer(myeditor, myProject);
179         }
180       }, 50, null);
181     }
182   }
183 }