filter out inspections, suppressed for file
[idea/community.git] / platform / lang-impl / src / com / intellij / codeInsight / daemon / impl / LocalInspectionsPass.java
1 /*
2  * Copyright 2000-2009 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
17 package com.intellij.codeInsight.daemon.impl;
18
19 import com.intellij.codeHighlighting.Pass;
20 import com.intellij.codeInsight.daemon.DaemonBundle;
21 import com.intellij.codeInsight.daemon.HighlightDisplayKey;
22 import com.intellij.codeInsight.daemon.impl.analysis.HighlightLevelUtil;
23 import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction;
24 import com.intellij.codeInsight.intention.EmptyIntentionAction;
25 import com.intellij.codeInspection.*;
26 import com.intellij.codeInspection.ex.*;
27 import com.intellij.codeInspection.ui.ProblemDescriptionNode;
28 import com.intellij.concurrency.JobUtil;
29 import com.intellij.injected.editor.DocumentWindow;
30 import com.intellij.lang.Language;
31 import com.intellij.lang.annotation.HighlightSeverity;
32 import com.intellij.lang.injection.InjectedLanguageManager;
33 import com.intellij.openapi.actionSystem.IdeActions;
34 import com.intellij.openapi.application.ApplicationManager;
35 import com.intellij.openapi.diagnostic.Logger;
36 import com.intellij.openapi.editor.Document;
37 import com.intellij.openapi.editor.RangeMarker;
38 import com.intellij.openapi.editor.colors.CodeInsightColors;
39 import com.intellij.openapi.editor.colors.EditorColorsScheme;
40 import com.intellij.openapi.editor.colors.TextAttributesKey;
41 import com.intellij.openapi.editor.markup.TextAttributes;
42 import com.intellij.openapi.keymap.Keymap;
43 import com.intellij.openapi.keymap.KeymapManager;
44 import com.intellij.openapi.keymap.KeymapUtil;
45 import com.intellij.openapi.progress.ProcessCanceledException;
46 import com.intellij.openapi.progress.ProgressIndicator;
47 import com.intellij.openapi.progress.ProgressManager;
48 import com.intellij.openapi.project.DumbAware;
49 import com.intellij.openapi.project.DumbService;
50 import com.intellij.openapi.util.TextRange;
51 import com.intellij.openapi.util.Trinity;
52 import com.intellij.openapi.util.text.StringUtil;
53 import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
54 import com.intellij.profile.codeInspection.SeverityProvider;
55 import com.intellij.psi.*;
56 import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
57 import com.intellij.util.ConcurrencyUtil;
58 import com.intellij.util.Function;
59 import com.intellij.util.Processor;
60 import com.intellij.util.containers.ConcurrentHashMap;
61 import com.intellij.util.containers.MultiMap;
62 import com.intellij.util.containers.TransferToEDTQueue;
63 import com.intellij.xml.util.XmlStringUtil;
64 import gnu.trove.THashMap;
65 import gnu.trove.THashSet;
66 import org.jetbrains.annotations.NonNls;
67 import org.jetbrains.annotations.NotNull;
68 import org.jetbrains.annotations.Nullable;
69
70 import java.util.*;
71 import java.util.concurrent.ConcurrentMap;
72
73 /**
74  * @author max
75  */
76 public class LocalInspectionsPass extends ProgressableTextEditorHighlightingPass implements DumbAware {
77   private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.LocalInspectionsPass");
78   public static final TextRange EMPTY_PRIORITY_RANGE = TextRange.EMPTY_RANGE;
79   private final int myStartOffset;
80   private final int myEndOffset;
81   private final TextRange myPriorityRange;
82   private final boolean myIgnoreSuppressed;
83   private final ConcurrentMap<PsiFile, List<InspectionResult>> result = new ConcurrentHashMap<PsiFile, List<InspectionResult>>();
84   private static final String PRESENTABLE_NAME = DaemonBundle.message("pass.inspection");
85   private volatile List<HighlightInfo> myInfos = Collections.emptyList();
86   private final String myShortcutText;
87   private final SeverityRegistrar mySeverityRegistrar;
88   private final InspectionProfileWrapper myProfileWrapper;
89   private boolean myFailFastOnAcquireReadAction;
90
91   public LocalInspectionsPass(@NotNull PsiFile file,
92                               @Nullable Document document,
93                               int startOffset,
94                               int endOffset,
95                               @NotNull TextRange priorityRange,
96                               boolean ignoreSuppressed) {
97     super(file.getProject(), document, PRESENTABLE_NAME, file, true);
98     myStartOffset = startOffset;
99     myEndOffset = endOffset;
100     myPriorityRange = priorityRange;
101     myIgnoreSuppressed = ignoreSuppressed;
102     setId(Pass.LOCAL_INSPECTIONS);
103
104     final KeymapManager keymapManager = KeymapManager.getInstance();
105     if (keymapManager != null) {
106       final Keymap keymap = keymapManager.getActiveKeymap();
107       myShortcutText = keymap == null ? "" : "(" + KeymapUtil.getShortcutsText(keymap.getShortcuts(IdeActions.ACTION_SHOW_ERROR_DESCRIPTION)) + ")";
108     }
109     else {
110       myShortcutText = "";
111     }
112     InspectionProfileWrapper profileToUse = InspectionProjectProfileManager.getInstance(myProject).getProfileWrapper();
113
114     Function<InspectionProfileWrapper,InspectionProfileWrapper> custom = file.getUserData(InspectionProfileWrapper.CUSTOMIZATION_KEY);
115     if (custom != null) {
116       profileToUse = custom.fun(profileToUse);
117     }
118
119     myProfileWrapper = profileToUse;
120     assert myProfileWrapper != null;
121     mySeverityRegistrar = ((SeverityProvider)myProfileWrapper.getInspectionProfile().getProfileManager()).getSeverityRegistrar();
122     LOG.assertTrue(mySeverityRegistrar != null);
123
124     // initial guess
125     setProgressLimit(300 * 2);
126   }
127
128   @Override
129   protected void collectInformationWithProgress(@NotNull ProgressIndicator progress) {
130     try {
131       if (!HighlightLevelUtil.shouldInspect(myFile)) return;
132       final InspectionManagerEx iManager = (InspectionManagerEx)InspectionManager.getInstance(myProject);
133       final InspectionProfileWrapper profile = myProfileWrapper;
134       inspect(getInspectionTools(profile), iManager, true, true, DumbService.isDumb(myProject), progress);
135     }
136     finally {
137       disposeDescriptors();
138     }
139   }
140
141   private void disposeDescriptors() {
142     result.clear();
143   }
144
145   public void doInspectInBatch(@NotNull InspectionManagerEx iManager, @NotNull List<InspectionProfileEntry> toolWrappers) {
146     Map<LocalInspectionTool, LocalInspectionToolWrapper> tool2Wrapper = new THashMap<LocalInspectionTool, LocalInspectionToolWrapper>(toolWrappers.size());
147     for (InspectionProfileEntry toolWrapper : toolWrappers) {
148       tool2Wrapper.put(((LocalInspectionToolWrapper)toolWrapper).getTool(), (LocalInspectionToolWrapper)toolWrapper);
149     }
150
151     ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator();
152     inspect(new ArrayList<LocalInspectionToolWrapper>(tool2Wrapper.values()), iManager, false, false, false, progress);
153     addDescriptorsFromInjectedResults(tool2Wrapper, iManager);
154     List<InspectionResult> resultList = result.get(myFile);
155     if (resultList == null) return;
156     for (InspectionResult inspectionResult : resultList) {
157       LocalInspectionTool tool = inspectionResult.tool;
158       LocalInspectionToolWrapper toolWrapper = tool2Wrapper.get(tool);
159       if (toolWrapper == null) continue;
160       for (ProblemDescriptor descriptor : inspectionResult.foundProblems) {
161         toolWrapper.addProblemDescriptors(Collections.singletonList(descriptor), myIgnoreSuppressed);
162       }
163     }
164   }
165
166   private void addDescriptorsFromInjectedResults(Map<LocalInspectionTool, LocalInspectionToolWrapper> tool2Wrapper, InspectionManagerEx iManager) {
167     InjectedLanguageManager ilManager = InjectedLanguageManager.getInstance(myProject);
168     PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject);
169
170     for (Map.Entry<PsiFile, List<InspectionResult>> entry : result.entrySet()) {
171       PsiFile file = entry.getKey();
172       if (file == myFile) continue; // not injected
173       DocumentWindow documentRange = (DocumentWindow)documentManager.getDocument(file);
174       List<InspectionResult> resultList = entry.getValue();
175       for (InspectionResult inspectionResult : resultList) {
176         LocalInspectionTool tool = inspectionResult.tool;
177         for (ProblemDescriptor descriptor : inspectionResult.foundProblems) {
178
179           PsiElement psiElement = descriptor.getPsiElement();
180           if (psiElement == null) continue;
181           if (InspectionManagerEx.inspectionResultSuppressed(psiElement, tool)) continue;
182           List<TextRange> editables = ilManager.intersectWithAllEditableFragments(file, ((ProblemDescriptorImpl)descriptor).getTextRange());
183           for (TextRange editable : editables) {
184             TextRange hostRange = documentRange.injectedToHost(editable);
185             QuickFix[] fixes = descriptor.getFixes();
186             LocalQuickFix[] localFixes = null;
187             if (fixes != null) {
188               localFixes = new LocalQuickFix[fixes.length];
189               for (int k = 0; k < fixes.length; k++) {
190                 QuickFix fix = fixes[k];
191                 localFixes[k] = (LocalQuickFix)fix;
192               }
193             }
194             ProblemDescriptor patchedDescriptor = iManager.createProblemDescriptor(myFile, hostRange, descriptor.getDescriptionTemplate(),
195                                                                                    descriptor.getHighlightType(), true, localFixes);
196             LocalInspectionToolWrapper toolWrapper = tool2Wrapper.get(tool);
197             toolWrapper.addProblemDescriptors(Collections.singletonList(patchedDescriptor), true);
198           }
199         }
200       }
201     }
202   }
203
204   private void inspect(@NotNull final List<LocalInspectionToolWrapper> toolWrappers,
205                        @NotNull final InspectionManagerEx iManager,
206                        final boolean isOnTheFly,
207                        boolean failFastOnAcquireReadAction,
208                        boolean checkDumbAwareness,
209                        @NotNull final ProgressIndicator indicator) {
210     myFailFastOnAcquireReadAction = failFastOnAcquireReadAction;
211     if (toolWrappers.isEmpty()) return;
212
213     List<PsiElement> inside = new ArrayList<PsiElement>();
214     List<PsiElement> outside = new ArrayList<PsiElement>();
215     Divider.divideInsideAndOutside(myFile, myStartOffset, myEndOffset, myPriorityRange, inside, outside,
216                                    HighlightLevelUtil.AnalysisLevel.HIGHLIGHT_AND_INSPECT,true);
217
218     MultiMap<LocalInspectionTool, String> tools = getToolsForElements(toolWrappers, checkDumbAwareness, inside, outside);
219
220     setProgressLimit(1L * tools.size() * 2);
221     final LocalInspectionToolSession session = new LocalInspectionToolSession(myFile, myStartOffset, myEndOffset);
222
223     List<InspectionContext> init =
224       visitPriorityElementsAndInit(tools, iManager, isOnTheFly, indicator, inside, session, toolWrappers, checkDumbAwareness);
225     visitRestElementsAndCleanup(iManager, isOnTheFly, indicator, outside, session, init, toolWrappers, checkDumbAwareness);
226
227     indicator.checkCanceled();
228
229     myInfos = new ArrayList<HighlightInfo>();
230     addHighlightsFromResults(myInfos, indicator);
231   }
232
233   private static MultiMap<LocalInspectionTool, String> getToolsForElements(List<LocalInspectionToolWrapper> toolWrappers,
234                                                                boolean checkDumbAwareness,
235                                                                List<PsiElement> inside, List<PsiElement> outside) {
236     Set<Language> languages = new HashSet<Language>();
237     for (PsiElement element : inside) {
238       languages.add(element.getLanguage());
239     }
240     for (PsiElement element : outside) {
241       languages.add(element.getLanguage());
242     }
243     Map<String, Language> langIds = new HashMap<String, Language>();
244     for (Language language : languages) {
245       langIds.put(language.getID(), language);
246     }
247     Set<String> dialects = new HashSet<String>();
248     for (Language language : languages) {
249       for (Language dialect : language.getDialects()) {
250         dialects.add(dialect.getID());
251       }
252     }
253     MultiMap<LocalInspectionTool, String> map = new MultiMap<LocalInspectionTool, String>() {
254       @Override
255       protected Collection<String> createCollection() {
256         return new HashSet<String>();
257       }
258
259       @Override
260       protected Collection<String> createEmptyCollection() {
261         return Collections.emptySet();
262       }
263     };
264     for (LocalInspectionToolWrapper wrapper : toolWrappers) {
265       String language = wrapper.getLanguage();
266       if (language == null) {
267         LocalInspectionTool tool = wrapper.getTool();
268         if (!checkDumbAwareness || tool instanceof DumbAware) {
269           map.put(tool, null);
270         }
271         continue;
272       }
273       Language lang = langIds.get(language);
274       if (lang != null) {
275         LocalInspectionTool tool = wrapper.getTool();
276         if (!checkDumbAwareness || tool instanceof DumbAware) {
277           map.putValue(tool, language);
278           if (wrapper.applyToDialects()) {
279             for (Language dialect : lang.getDialects()) {
280               map.putValue(tool, dialect.getID());
281             }
282           }
283         }
284       }
285       else if (wrapper.applyToDialects() && dialects.contains(language)) {
286         LocalInspectionTool tool = wrapper.getTool();
287         if (!checkDumbAwareness || tool instanceof DumbAware) {
288           map.putValue(tool, language);
289         }
290       }
291     }
292     return map;
293   }
294
295   private List<InspectionContext> visitPriorityElementsAndInit(@NotNull MultiMap<LocalInspectionTool, String> tools,
296                                             @NotNull final InspectionManagerEx iManager,
297                                             final boolean isOnTheFly,
298                                             @NotNull final ProgressIndicator indicator,
299                                             @NotNull final List<PsiElement> elements,
300                                             @NotNull final LocalInspectionToolSession session,
301                                             List<LocalInspectionToolWrapper> wrappers, boolean checkDumbAwareness) {
302
303     final ArrayList<InspectionContext> init = new ArrayList<InspectionContext>();
304     List<Map.Entry<LocalInspectionTool, Collection<String>>> entries = new ArrayList<Map.Entry<LocalInspectionTool, Collection<String>>>(tools.entrySet());
305     boolean result = JobUtil.invokeConcurrentlyUnderProgress(entries, indicator, myFailFastOnAcquireReadAction, new Processor<Map.Entry<LocalInspectionTool, Collection<String>>>() {
306       @Override
307       public boolean process(final Map.Entry<LocalInspectionTool, Collection<String>> pair) {
308         indicator.checkCanceled();
309
310         ApplicationManager.getApplication().assertReadAccessAllowed();
311         final LocalInspectionTool tool = pair.getKey();
312         final boolean[] applyIncrementally = {isOnTheFly};
313         ProblemsHolder holder = new ProblemsHolder(iManager, myFile, isOnTheFly) {
314           @Override
315           public void registerProblem(@NotNull ProblemDescriptor descriptor) {
316             super.registerProblem(descriptor);
317             if (applyIncrementally[0]) {
318               addDescriptorIncrementally(descriptor, tool, indicator);
319             }
320           }
321         };
322         PsiElementVisitor visitor = createVisitorAndAcceptElements(tool, holder, isOnTheFly, session, elements,
323                                                                    (Set<String>)pair.getValue());
324
325         synchronized (init) {
326           init.add(new InspectionContext(tool, holder, visitor, (Set<String>)pair.getValue()));
327         }
328         advanceProgress(1);
329
330         if (holder.hasResults()) {
331           appendDescriptors(myFile, holder.getResults(), tool);
332         }
333         applyIncrementally[0] = false; // do not apply incrementally outside visible range
334         return true;
335       }
336     });
337     if (!result) throw new ProcessCanceledException();
338     inspectInjectedPsi(elements, isOnTheFly, indicator, iManager, true, checkDumbAwareness, wrappers);
339     return init;
340   }
341
342   private static PsiElementVisitor createVisitorAndAcceptElements(@NotNull LocalInspectionTool tool,
343                                                                   @NotNull ProblemsHolder holder,
344                                                                   boolean isOnTheFly,
345                                                                   @NotNull LocalInspectionToolSession session,
346                                                                   @NotNull List<PsiElement> elements,
347                                                                   @Nullable Set<String> languages) {
348     PsiElementVisitor visitor = tool.buildVisitor(holder, isOnTheFly, session);
349     //noinspection ConstantConditions
350     if(visitor == null) {
351       LOG.error("Tool " + tool + " must not return null from the buildVisitor() method");
352     }
353     assert !(visitor instanceof PsiRecursiveElementVisitor || visitor instanceof PsiRecursiveElementWalkingVisitor)
354       : "The visitor returned from LocalInspectionTool.buildVisitor() must not be recursive. "+tool;
355
356     tool.inspectionStarted(session, isOnTheFly);
357     acceptElements(elements, visitor, languages);
358     return visitor;
359   }
360
361   private void visitRestElementsAndCleanup(@NotNull final InspectionManagerEx iManager,
362                                            final boolean isOnTheFly,
363                                            @NotNull final ProgressIndicator indicator,
364                                            @NotNull final List<PsiElement> elements,
365                                            @NotNull final LocalInspectionToolSession session,
366                                            @NotNull List<InspectionContext> init,
367                                            List<LocalInspectionToolWrapper> wrappers, boolean checkDumbAwareness) {
368     Processor<InspectionContext> processor =
369       new Processor<InspectionContext>() {
370         @Override
371         public boolean process(InspectionContext context) {
372           
373           indicator.checkCanceled();
374           ApplicationManager.getApplication().assertReadAccessAllowed();
375           acceptElements(elements, context.visitor, context.languageIds);
376           advanceProgress(1);
377           context.tool.inspectionFinished(session, context.holder);
378
379           if (context.holder.hasResults()) {
380             appendDescriptors(myFile, context.holder.getResults(), context.tool);
381           }
382           return true;
383         }
384       };
385     boolean result = JobUtil.invokeConcurrentlyUnderProgress(init, indicator, myFailFastOnAcquireReadAction, processor);
386     if (!result) {
387       throw new ProcessCanceledException();
388     }
389     inspectInjectedPsi(elements, isOnTheFly, indicator, iManager, false, checkDumbAwareness, wrappers);
390   }
391
392   private static void acceptElements(@NotNull List<PsiElement> elements,
393                                      @NotNull PsiElementVisitor elementVisitor,
394                                      @Nullable Set<String> languages) {
395     //noinspection ForLoopReplaceableByForEach
396     for (int i = 0, elementsSize = elements.size(); i < elementsSize; i++) {
397       PsiElement element = elements.get(i);
398       if (languages == null || languages.contains(element.getLanguage().getID())) {
399         element.accept(elementVisitor);
400       }
401       ProgressManager.checkCanceled();
402     }
403   }
404
405   void inspectInjectedPsi(@NotNull final List<PsiElement> elements,
406                           final boolean onTheFly,
407                           @NotNull final ProgressIndicator indicator,
408                           @NotNull final InspectionManagerEx iManager,
409                           final boolean inVisibleRange, final boolean checkDumbAwareness, final List<LocalInspectionToolWrapper> wrappers) {
410     final Set<PsiFile> injected = new THashSet<PsiFile>();
411     for (PsiElement element : elements) {
412       InjectedLanguageUtil.enumerate(element, myFile, false, new PsiLanguageInjectionHost.InjectedPsiVisitor() {
413         @Override
414         public void visit(@NotNull PsiFile injectedPsi, @NotNull List<PsiLanguageInjectionHost.Shred> places) {
415           injected.add(injectedPsi);
416         }
417       });
418     }
419     if (injected.isEmpty()) return;
420     if (!JobUtil.invokeConcurrentlyUnderProgress(new ArrayList<PsiFile>(injected), indicator, myFailFastOnAcquireReadAction, new Processor<PsiFile>() {
421       @Override
422       public boolean process(final PsiFile injectedPsi) {
423         doInspectInjectedPsi(injectedPsi, onTheFly, indicator, iManager, inVisibleRange, wrappers, checkDumbAwareness);
424         return true;
425       }
426     })) throw new ProcessCanceledException();
427   }
428
429   @Nullable
430   private HighlightInfo highlightInfoFromDescriptor(@NotNull ProblemDescriptor problemDescriptor,
431                                                     @NotNull HighlightInfoType highlightInfoType,
432                                                     @NotNull String message,
433                                                     String toolTip, PsiElement psiElement) {
434     TextRange textRange = ((ProblemDescriptorImpl)problemDescriptor).getTextRange();
435     if (textRange == null || psiElement == null) return null;
436     boolean isFileLevel = psiElement instanceof PsiFile && textRange.equals(psiElement.getTextRange());
437
438     final HighlightSeverity severity = highlightInfoType.getSeverity(psiElement);
439     TextAttributes attributes = mySeverityRegistrar.getTextAttributesBySeverity(severity);
440     return new HighlightInfo(attributes, highlightInfoType, textRange.getStartOffset(),
441                              textRange.getEndOffset(), message, toolTip,
442                              severity, problemDescriptor.isAfterEndOfLine(), null, isFileLevel);
443   }
444
445   private final Map<TextRange, RangeMarker> ranges2markersCache = new THashMap<TextRange, RangeMarker>();
446   private final TransferToEDTQueue<Trinity<ProblemDescriptor, LocalInspectionTool,ProgressIndicator>> myTransferToEDTQueue
447     = new TransferToEDTQueue<Trinity<ProblemDescriptor, LocalInspectionTool,ProgressIndicator>>("Apply inspection results", new Processor<Trinity<ProblemDescriptor, LocalInspectionTool,ProgressIndicator>>() {
448     private final InspectionProfile inspectionProfile = InspectionProjectProfileManager.getInstance(myProject).getInspectionProfile();
449     private final InjectedLanguageManager ilManager = InjectedLanguageManager.getInstance(myProject);
450     private final List<HighlightInfo> infos = new ArrayList<HighlightInfo>(2);
451     private final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject);
452     @Override
453     public boolean process(Trinity<ProblemDescriptor, LocalInspectionTool,ProgressIndicator> trinity) {
454       ProgressIndicator indicator = trinity.getThird();
455       if (indicator.isCanceled()) {
456         return false;
457       }
458
459       ProblemDescriptor descriptor = trinity.first;
460       LocalInspectionTool tool = trinity.second;
461       PsiElement psiElement = descriptor.getPsiElement();
462       if (psiElement == null) return true;
463       PsiFile file = psiElement.getContainingFile();
464       Document thisDocument = documentManager.getDocument(file);
465
466       HighlightSeverity severity = inspectionProfile.getErrorLevel(HighlightDisplayKey.find(tool.getShortName()), file).getSeverity();
467
468       infos.clear();
469       createHighlightsForDescriptor(infos, emptyActionRegistered, ilManager, file, thisDocument, tool, severity, descriptor, psiElement);
470       for (HighlightInfo info : infos) {
471         final EditorColorsScheme colorsScheme = getColorsScheme();
472         UpdateHighlightersUtil.addHighlighterToEditorIncrementally(myProject, myDocument, myFile, myStartOffset, myEndOffset,
473                                                                    info, colorsScheme, getId(), ranges2markersCache);
474       }
475
476       return true;
477     }
478   }, myProject.getDisposed(), 200);
479
480   private final Set<TextRange> emptyActionRegistered = Collections.synchronizedSet(new HashSet<TextRange>());
481
482   private void addDescriptorIncrementally(@NotNull final ProblemDescriptor descriptor,
483                                           @NotNull final LocalInspectionTool tool,
484                                           @NotNull final ProgressIndicator indicator) {
485     if (myIgnoreSuppressed && InspectionManagerEx.inspectionResultSuppressed(descriptor.getPsiElement(), tool)) {
486       return;
487     }
488     myTransferToEDTQueue.offer(Trinity.create(descriptor, tool, indicator));
489   }
490
491   private void appendDescriptors(PsiFile file, List<ProblemDescriptor> descriptors, LocalInspectionTool tool) {
492     for (ProblemDescriptor descriptor : descriptors) {
493       if (descriptor == null) {
494         LOG.error("null descriptor. all descriptors(" + descriptors.size() +"): " +
495                    descriptors + "; file: " + file + " (" + file.getVirtualFile() +"); tool: " + tool);
496       }
497     }
498     InspectionResult res = new InspectionResult(tool, descriptors);
499     appendResult(file, res);
500   }
501
502   private void appendResult(PsiFile file, InspectionResult res) {
503     List<InspectionResult> resultList = result.get(file);
504     if (resultList == null) {
505       resultList = ConcurrencyUtil.cacheOrGet(result, file, new ArrayList<InspectionResult>());
506     }
507     synchronized (resultList) {
508       resultList.add(res);
509     }
510   }
511
512   @NotNull
513   private HighlightInfoType highlightTypeFromDescriptor(final ProblemDescriptor problemDescriptor, final HighlightSeverity severity) {
514     final ProblemHighlightType highlightType = problemDescriptor.getHighlightType();
515     switch (highlightType) {
516       case GENERIC_ERROR_OR_WARNING:
517         return mySeverityRegistrar.getHighlightInfoTypeBySeverity(severity);
518       case LIKE_DEPRECATED:
519         return new HighlightInfoType.HighlightInfoTypeImpl(severity, HighlightInfoType.DEPRECATED.getAttributesKey());
520       case LIKE_UNKNOWN_SYMBOL:
521         if (severity == HighlightSeverity.ERROR) {
522           return new HighlightInfoType.HighlightInfoTypeImpl(severity, HighlightInfoType.WRONG_REF.getAttributesKey());
523         }
524         else if (severity == HighlightSeverity.WARNING) {
525           return new HighlightInfoType.HighlightInfoTypeImpl(severity, CodeInsightColors.WEAK_WARNING_ATTRIBUTES);
526         }
527         else {
528           return mySeverityRegistrar.getHighlightInfoTypeBySeverity(severity);
529         }
530       case LIKE_UNUSED_SYMBOL:
531         return new HighlightInfoType.HighlightInfoTypeImpl(severity, HighlightInfoType.UNUSED_SYMBOL.getAttributesKey());
532       case INFO:
533         return HighlightInfoType.INFO;
534        case WEAK_WARNING:
535         return HighlightInfoType.WEAK_WARNING;
536       case ERROR:
537         return HighlightInfoType.WRONG_REF;
538       case GENERIC_ERROR:
539         return HighlightInfoType.ERROR;
540       case INFORMATION:
541         final TextAttributesKey attributes = ((ProblemDescriptorImpl)problemDescriptor).getEnforcedTextAttributes();
542         if (attributes != null) {
543           return new HighlightInfoType.HighlightInfoTypeImpl(HighlightSeverity.INFORMATION, attributes);
544         }
545         return HighlightInfoType.INFORMATION;
546     }
547     throw new RuntimeException("Cannot map " + highlightType);
548   }
549
550   @Override
551   protected void applyInformationWithProgress() {
552     UpdateHighlightersUtil.setHighlightersToEditor(myProject, myDocument, myStartOffset, myEndOffset, myInfos, getColorsScheme(), getId());
553   }
554
555   private void addHighlightsFromResults(@NotNull List<HighlightInfo> outInfos, @NotNull ProgressIndicator indicator) {
556     InspectionProfile inspectionProfile = InspectionProjectProfileManager.getInstance(myProject).getInspectionProfile();
557     PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject);
558     InjectedLanguageManager ilManager = InjectedLanguageManager.getInstance(myProject);
559     Set<TextRange> emptyActionRegistered = new THashSet<TextRange>();
560
561     for (Map.Entry<PsiFile, List<InspectionResult>> entry : result.entrySet()) {
562       indicator.checkCanceled();
563       PsiFile file = entry.getKey();
564       Document documentRange = documentManager.getDocument(file);
565       if (documentRange == null) continue;
566       List<InspectionResult> resultList = entry.getValue();
567       synchronized (resultList) {
568         for (InspectionResult inspectionResult : resultList) {
569           indicator.checkCanceled();
570           LocalInspectionTool tool = inspectionResult.tool;
571           HighlightSeverity severity = inspectionProfile.getErrorLevel(HighlightDisplayKey.find(tool.getShortName()), file).getSeverity();
572           for (ProblemDescriptor descriptor : inspectionResult.foundProblems) {
573             indicator.checkCanceled();
574             PsiElement element = descriptor.getPsiElement();
575             createHighlightsForDescriptor(outInfos, emptyActionRegistered, ilManager, file, documentRange, tool, severity, descriptor, element);
576           }
577         }
578       }
579     }
580   }
581
582   private void createHighlightsForDescriptor(List<HighlightInfo> outInfos,
583                                              Set<TextRange> emptyActionRegistered,
584                                              InjectedLanguageManager ilManager,
585                                              PsiFile file,
586                                              Document documentRange,
587                                              LocalInspectionTool tool,
588                                              HighlightSeverity severity,
589                                              ProblemDescriptor descriptor,
590                                              PsiElement element) {
591     if (element == null) return;
592     if (myIgnoreSuppressed && InspectionManagerEx.inspectionResultSuppressed(element, tool)) return;
593     HighlightInfoType level = highlightTypeFromDescriptor(descriptor, severity);
594     HighlightInfo info = createHighlightInfo(descriptor, tool, level, emptyActionRegistered, element);
595     if (info == null) return;
596
597     if (file == myFile) {
598       // not injected
599       outInfos.add(info);
600       return;
601     }
602     // todo we got to separate our "internal" prefixes/suffixes from user-defined ones
603     // todo in the latter case the errors should be highlighted, otherwise not
604     List<TextRange> editables = ilManager.intersectWithAllEditableFragments(file, new TextRange(info.startOffset, info.endOffset));
605     for (TextRange editable : editables) {
606       TextRange hostRange = ((DocumentWindow)documentRange).injectedToHost(editable);
607       HighlightInfo patched = HighlightInfo.createHighlightInfo(info.type, element, hostRange.getStartOffset(),
608                                                                 hostRange.getEndOffset(), info.description, info.toolTip);
609       if (patched != null &&
610           (patched.startOffset != patched.endOffset ||
611            info.startOffset == info.endOffset)) {
612         patched.fromInjection = true;
613         registerQuickFixes(tool, descriptor, patched, emptyActionRegistered);
614         outInfos.add(patched);
615       }
616     }
617   }
618
619   @Nullable
620   private HighlightInfo createHighlightInfo(@NotNull ProblemDescriptor descriptor,
621                                             @NotNull LocalInspectionTool tool,
622                                             @NotNull HighlightInfoType level,
623                                             @NotNull Set<TextRange> emptyActionRegistered,
624                                             @NotNull PsiElement element) {
625     @NonNls String message = ProblemDescriptionNode.renderDescriptionMessage(descriptor, element);
626
627     final HighlightDisplayKey key = HighlightDisplayKey.find(tool.getShortName());
628     final InspectionProfile inspectionProfile = myProfileWrapper.getInspectionProfile();
629     if (!inspectionProfile.isToolEnabled(key, myFile)) return null;
630
631     HighlightInfoType type = new HighlightInfoType.HighlightInfoTypeImpl(level.getSeverity(element), level.getAttributesKey());
632     final String plainMessage = message.startsWith("<html>") ? StringUtil.unescapeXml(message.replaceAll("<[^>]*>", "")) : message;
633     @NonNls final String link = " <a href=\"#inspection/" + tool.getShortName() + "\">" + DaemonBundle.message("inspection.extended.description") +
634                                 "</a> " + myShortcutText;
635
636     @NonNls String tooltip = null;
637     if (descriptor.showTooltip()) {
638       if (message.startsWith("<html>")) {
639         tooltip = message.contains("</body>") ? message.replace("</body>", link + "</body>") : message.replace("</html>", link + "</html>");
640       }
641       else {
642         tooltip = "<html><body>" + XmlStringUtil.escapeString(message) + link + "</body></html>";
643       }
644     }
645     HighlightInfo highlightInfo = highlightInfoFromDescriptor(descriptor, type, plainMessage, tooltip,element);
646     if (highlightInfo != null) {
647       registerQuickFixes(tool, descriptor, highlightInfo, emptyActionRegistered);
648     }
649     return highlightInfo;
650   }
651
652   private static void registerQuickFixes(final LocalInspectionTool tool,
653                                          final ProblemDescriptor descriptor,
654                                          @NotNull HighlightInfo highlightInfo,
655                                          final Set<TextRange> emptyActionRegistered) {
656     final HighlightDisplayKey key = HighlightDisplayKey.find(tool.getShortName());
657     boolean needEmptyAction = true;
658     final QuickFix[] fixes = descriptor.getFixes();
659     if (fixes != null && fixes.length > 0) {
660       for (int k = 0; k < fixes.length; k++) {
661         if (fixes[k] != null) { // prevent null fixes from var args
662           QuickFixAction.registerQuickFixAction(highlightInfo, QuickFixWrapper.wrap(descriptor, k), key);
663           needEmptyAction = false;
664         }
665       }
666     }
667     HintAction hintAction = ((ProblemDescriptorImpl)descriptor).getHintAction();
668     if (hintAction != null) {
669       QuickFixAction.registerQuickFixAction(highlightInfo, hintAction, key);
670       needEmptyAction = false;
671     }
672     if (((ProblemDescriptorImpl)descriptor).getEnforcedTextAttributes() != null) {
673       needEmptyAction = false;
674     }
675     if (needEmptyAction && emptyActionRegistered.add(new TextRange(highlightInfo.fixStartOffset, highlightInfo.fixEndOffset))) {
676       EmptyIntentionAction emptyIntentionAction = new EmptyIntentionAction(tool.getDisplayName());
677       QuickFixAction.registerQuickFixAction(highlightInfo, emptyIntentionAction, key);
678     }
679   }
680
681   private static List<PsiElement> getElementsFrom(PsiFile file) {
682     final FileViewProvider viewProvider = file.getViewProvider();
683     final Set<PsiElement> result = new LinkedHashSet<PsiElement>();
684     final PsiElementVisitor visitor = new PsiRecursiveElementVisitor() {
685       @Override public void visitElement(PsiElement element) {
686         ProgressManager.checkCanceled();
687         PsiElement child = element.getFirstChild();
688         if (child == null) {
689           // leaf element
690         }
691         else {
692           // composite element
693           while (child != null) {
694             child.accept(this);
695             result.add(child);
696
697             child = child.getNextSibling();
698           }
699         }
700       }
701     };
702     for (Language language : viewProvider.getLanguages()) {
703       final PsiFile psiRoot = viewProvider.getPsi(language);
704       if (psiRoot == null || !HighlightLevelUtil.shouldInspect(psiRoot)) {
705         continue;
706       }
707       psiRoot.accept(visitor);
708       result.add(psiRoot);
709     }
710     return new ArrayList<PsiElement>(result);
711   }
712
713   List<LocalInspectionToolWrapper> getInspectionTools(InspectionProfileWrapper profile) {
714     final List<LocalInspectionToolWrapper> tools = profile.getHighlightingLocalInspectionTools(myFile);
715     for (Iterator<LocalInspectionToolWrapper> iterator = tools.iterator(); iterator.hasNext(); ) {
716       LocalInspectionToolWrapper tool = iterator.next();
717       if (myIgnoreSuppressed && InspectionManagerEx.inspectionResultSuppressed(myFile, tool.getTool())) {
718         iterator.remove();
719       }
720     }
721     return tools;
722   }
723
724   private void doInspectInjectedPsi(@NotNull PsiFile injectedPsi,
725                                     final boolean isOnTheFly,
726                                     @NotNull final ProgressIndicator indicator,
727                                     @NotNull InspectionManagerEx iManager,
728                                     final boolean inVisibleRange, List<LocalInspectionToolWrapper> wrappers, boolean checkDumbAwareness) {
729     final PsiElement host = injectedPsi.getContext();
730
731     final List<PsiElement> elements = getElementsFrom(injectedPsi);
732     if (elements.isEmpty()) {
733       return;
734     }
735     MultiMap<LocalInspectionTool, String> tools =
736       getToolsForElements(wrappers, checkDumbAwareness, elements, Collections.<PsiElement>emptyList());
737     for (final Map.Entry<LocalInspectionTool, Collection<String>> pair : tools.entrySet()) {
738       indicator.checkCanceled();
739       final LocalInspectionTool tool = pair.getKey();
740       if (host != null && myIgnoreSuppressed && InspectionManagerEx.inspectionResultSuppressed(host, tool)) {
741         continue;
742       }
743       ProblemsHolder holder = new ProblemsHolder(iManager, injectedPsi, isOnTheFly) {
744         @Override
745         public void registerProblem(@NotNull ProblemDescriptor descriptor) {
746           super.registerProblem(descriptor);
747           if (isOnTheFly && inVisibleRange) {
748             addDescriptorIncrementally(descriptor, tool, indicator);
749           }
750         }
751       };
752
753       LocalInspectionToolSession injSession = new LocalInspectionToolSession(injectedPsi, 0, injectedPsi.getTextLength());
754       createVisitorAndAcceptElements(tool, holder, isOnTheFly, injSession, elements, (Set<String>)pair.getValue());
755       tool.inspectionFinished(injSession, holder);
756       List<ProblemDescriptor> problems = holder.getResults();
757       if (!problems.isEmpty()) {
758         appendDescriptors(injectedPsi, problems, tool);
759       }
760     }
761   }
762
763   @Override
764   @NotNull
765   public List<HighlightInfo> getInfos() {
766     return myInfos;
767   }
768
769   private static class InspectionResult {
770     public final LocalInspectionTool tool;
771     public final List<ProblemDescriptor> foundProblems;
772
773     private InspectionResult(@NotNull LocalInspectionTool tool, @NotNull List<ProblemDescriptor> foundProblems) {
774       this.tool = tool;
775       this.foundProblems = foundProblems;
776     }
777   }
778   
779   private static class InspectionContext {
780     
781     private InspectionContext(LocalInspectionTool tool, ProblemsHolder holder, PsiElementVisitor visitor, Set<String> languageIds) {
782       this.tool = tool;
783       this.holder = holder;
784       this.visitor = visitor;
785       this.languageIds = languageIds;
786     }
787
788     final LocalInspectionTool tool;
789     final ProblemsHolder holder;
790     final PsiElementVisitor visitor;
791     final Set<String> languageIds;
792   }
793 }