15d2d5e8375416a1ce436795001aab57dbda2248
[idea/community.git] / platform / lang-impl / src / com / intellij / codeInsight / daemon / impl / DaemonCodeAnalyzerImpl.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.BackgroundEditorHighlighter;
20 import com.intellij.codeHighlighting.HighlightingPass;
21 import com.intellij.codeHighlighting.Pass;
22 import com.intellij.codeHighlighting.TextEditorHighlightingPass;
23 import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
24 import com.intellij.codeInsight.daemon.DaemonCodeAnalyzerSettings;
25 import com.intellij.codeInsight.daemon.LineMarkerInfo;
26 import com.intellij.codeInsight.daemon.ReferenceImporter;
27 import com.intellij.codeInsight.hint.HintManager;
28 import com.intellij.codeInsight.intention.impl.IntentionHintComponent;
29 import com.intellij.concurrency.Job;
30 import com.intellij.ide.PowerSaveMode;
31 import com.intellij.lang.annotation.HighlightSeverity;
32 import com.intellij.openapi.application.ApplicationManager;
33 import com.intellij.openapi.diagnostic.Logger;
34 import com.intellij.openapi.editor.Document;
35 import com.intellij.openapi.editor.Editor;
36 import com.intellij.openapi.editor.ex.DocumentEx;
37 import com.intellij.openapi.editor.ex.EditorMarkupModel;
38 import com.intellij.openapi.editor.ex.MarkupModelEx;
39 import com.intellij.openapi.editor.ex.RangeHighlighterEx;
40 import com.intellij.openapi.editor.markup.MarkupModel;
41 import com.intellij.openapi.editor.markup.RangeHighlighter;
42 import com.intellij.openapi.extensions.Extensions;
43 import com.intellij.openapi.fileEditor.FileEditor;
44 import com.intellij.openapi.fileEditor.TextEditor;
45 import com.intellij.openapi.fileTypes.FileType;
46 import com.intellij.openapi.fileTypes.StdFileTypes;
47 import com.intellij.openapi.progress.ProgressIndicator;
48 import com.intellij.openapi.progress.ProgressManager;
49 import com.intellij.openapi.project.Project;
50 import com.intellij.openapi.util.*;
51 import com.intellij.openapi.vfs.VirtualFile;
52 import com.intellij.openapi.vfs.VirtualFileManager;
53 import com.intellij.packageDependencies.DependencyValidationManager;
54 import com.intellij.psi.PsiCompiledElement;
55 import com.intellij.psi.PsiDocumentManager;
56 import com.intellij.psi.PsiFile;
57 import com.intellij.psi.PsiFileSystemItem;
58 import com.intellij.psi.search.scope.packageSet.NamedScope;
59 import com.intellij.psi.search.scope.packageSet.NamedScopeManager;
60 import com.intellij.psi.search.scope.packageSet.NamedScopesHolder;
61 import com.intellij.util.Alarm;
62 import com.intellij.util.CommonProcessors;
63 import com.intellij.util.Processor;
64 import com.intellij.util.SmartList;
65 import com.intellij.util.containers.ContainerUtil;
66 import com.intellij.util.ui.UIUtil;
67 import gnu.trove.THashMap;
68 import gnu.trove.THashSet;
69 import org.jdom.Element;
70 import org.jetbrains.annotations.NonNls;
71 import org.jetbrains.annotations.NotNull;
72 import org.jetbrains.annotations.Nullable;
73 import org.jetbrains.annotations.TestOnly;
74
75 import javax.swing.*;
76 import java.util.*;
77
78 /**
79  * This class also controls the auto-reparse and auto-hints.
80  */
81 public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMExternalizable {
82   private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl");
83
84   private static final Key<List<LineMarkerInfo>> MARKERS_IN_EDITOR_DOCUMENT_KEY = Key.create("MARKERS_IN_EDITOR_DOCUMENT");
85   private final Project myProject;
86   private final DaemonCodeAnalyzerSettings mySettings;
87   private final EditorTracker myEditorTracker;
88   private DaemonProgressIndicator myUpdateProgress = new DaemonProgressIndicator(); //guarded by this
89
90   private final Runnable myUpdateRunnable = createUpdateRunnable();
91
92   private final Alarm myAlarm = new Alarm();
93   private boolean myUpdateByTimerEnabled = true;
94   private final Collection<VirtualFile> myDisabledHintsFiles = new THashSet<VirtualFile>();
95   private final Collection<PsiFile> myDisabledHighlightingFiles = new THashSet<PsiFile>();
96
97   private final FileStatusMap myFileStatusMap;
98   private DaemonCodeAnalyzerSettings myLastSettings;
99
100   private IntentionHintComponent myLastIntentionHint; //guarded by this
101   private volatile boolean myDisposed;
102   private boolean myInitialized;
103
104   @NonNls private static final String DISABLE_HINTS_TAG = "disable_hints";
105   @NonNls private static final String FILE_TAG = "file";
106   @NonNls private static final String URL_ATT = "url";
107   private DaemonListeners myDaemonListeners;
108   private StatusBarUpdater myStatusBarUpdater;
109   private final PassExecutorService myPassExecutorService;
110   private int myModificationCount = 0;
111
112   //   @TestOnly
113   private boolean allowToInterrupt = true;
114
115   public DaemonCodeAnalyzerImpl(Project project, DaemonCodeAnalyzerSettings daemonCodeAnalyzerSettings, EditorTracker editorTracker) {
116     myProject = project;
117
118     mySettings = daemonCodeAnalyzerSettings;
119     myEditorTracker = editorTracker;
120     myLastSettings = (DaemonCodeAnalyzerSettings)mySettings.clone();
121
122     myFileStatusMap = new FileStatusMap(myProject);
123     myPassExecutorService = new PassExecutorService(myProject) {
124       protected void afterApplyInformationToEditor(final TextEditorHighlightingPass pass,
125                                                    final FileEditor fileEditor,
126                                                    final ProgressIndicator updateProgress) {
127         if (fileEditor instanceof TextEditor) {
128           log(updateProgress, pass, "Apply ");
129           Editor editor = ((TextEditor)fileEditor).getEditor();
130           repaintErrorStripeRenderer(editor);
131         }
132       }
133
134       protected boolean isDisposed() {
135         return myDisposed || super.isDisposed();
136       }
137     };
138     Disposer.register(project, myPassExecutorService);
139     Disposer.register(project, myFileStatusMap);
140   }
141
142   static boolean hasErrors(Project project, Document document) {
143     return !processHighlights(document, project, HighlightSeverity.ERROR, 0, document.getTextLength(), CommonProcessors.<HighlightInfo>alwaysFalse());
144   }
145
146   @NotNull
147   @TestOnly
148   public static List<HighlightInfo> getHighlights(Document document, HighlightSeverity minSeverity, Project project) {
149     List<HighlightInfo> infos = new ArrayList<HighlightInfo>();
150     processHighlights(document, project, minSeverity, 0, document.getTextLength(), new CommonProcessors.CollectProcessor<HighlightInfo>(infos));
151     return infos;
152   }
153
154   public List<HighlightInfo> runMainPasses(@NotNull PsiFile psiFile,
155                                            @NotNull Document document,
156                                            @NotNull final ProgressIndicator progress) {
157     GeneralHighlightingPass action1 = new GeneralHighlightingPass(myProject, psiFile, document, 0, psiFile.getTextLength(), true);
158     action1.doCollectInformation(progress);
159
160     List<HighlightInfo> result = new ArrayList<HighlightInfo>();
161     result.addAll(action1.getHighlights());
162
163     LocalInspectionsPass action3 = new LocalInspectionsPass(psiFile, document, 0, psiFile.getTextLength());
164     action3.doCollectInformation(progress);
165
166     result.addAll(action3.getHighlights());
167
168     return result;
169   }
170
171   @TestOnly
172   public List<HighlightInfo> runPasses(@NotNull PsiFile file,
173                                        @NotNull Document document,
174                                        @NotNull TextEditor textEditor,
175                                        @NotNull final ProgressIndicator progress,
176                                        @NotNull int[] toIgnore,
177                                        boolean allowDirt,
178                                        final boolean apply) {
179     // pump first so that queued event do not interfere
180     if (SwingUtilities.isEventDispatchThread()) {
181       UIUtil.dispatchAllInvocationEvents();
182     }
183     else {
184       UIUtil.pump();
185     }
186
187     Project project = file.getProject();
188     setUpdateByTimerEnabled(false);
189     FileStatusMap fileStatusMap = getFileStatusMap();
190     for (int ignoreId : toIgnore) {
191       fileStatusMap.markFileUpToDate(document, file, ignoreId);
192     }
193     fileStatusMap.allowDirt(allowDirt);
194     try {
195       TextEditorBackgroundHighlighter highlighter = (TextEditorBackgroundHighlighter)textEditor.getBackgroundHighlighter();
196       final List<TextEditorHighlightingPass> passes = highlighter.getPasses(toIgnore);
197       ProgressManager.getInstance().runProcess(new Runnable() {
198         public void run() {
199           for (TextEditorHighlightingPass pass : passes) {
200             pass.collectInformation(progress);
201             if (apply) {
202               // apply incremental highlights scheduled for AWT thread
203               if (SwingUtilities.isEventDispatchThread()) {
204                 UIUtil.dispatchAllInvocationEvents();
205               }
206               else {
207                 UIUtil.pump();
208               }
209               pass.applyInformationToEditor();
210             }
211           }
212         }
213       }, progress);
214
215       return getHighlights(document, null, project);
216     }
217     finally {
218       fileStatusMap.allowDirt(true);
219       myPassExecutorService.cancelAll(true);
220     }
221   }
222
223   @NotNull
224   public String getComponentName() {
225     return "DaemonCodeAnalyzer";
226   }
227
228   public void initComponent() {
229   }
230
231   public void disposeComponent() {
232   }
233
234   public void projectOpened() {
235     assert !myInitialized : "Double Initializing";
236     myStatusBarUpdater = new StatusBarUpdater(myProject);
237     Disposer.register(myProject, myStatusBarUpdater);
238
239     myDaemonListeners = new DaemonListeners(myProject, this, myEditorTracker);
240     Disposer.register(myProject, myDaemonListeners);
241     reloadScopes();
242
243     myInitialized = true;
244     myDisposed = false;
245     myFileStatusMap.markAllFilesDirty();
246   }
247
248   public void projectClosed() {
249     assert myInitialized : "Disposing not initialized component";
250     assert !myDisposed : "Double dispose";
251
252     // clear dangling references to PsiFiles/Documents. SCR#10358
253     myFileStatusMap.markAllFilesDirty();
254
255     stopProcess(false);
256
257     myDisposed = true;
258     myLastSettings = null;
259     myInitialized = false;
260   }
261
262   @TestOnly
263   public boolean isInitialized() {
264     return myInitialized;
265   }
266
267   void repaintErrorStripeRenderer(Editor editor) {
268     if (!myProject.isInitialized()) return;
269     final Document document = editor.getDocument();
270     final PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(document);
271     final EditorMarkupModel markup = (EditorMarkupModel)editor.getMarkupModel();
272     markup.setErrorStripeRenderer(new TrafficLightRenderer(myProject, this, document, psiFile));
273     markup.setErrorPanelPopupHandler(new DaemonEditorPopup(psiFile));
274     markup.setErrorStripTooltipRendererProvider(new DaemonTooltipRendererProvider(myProject));
275     markup.setMinMarkHeight(DaemonCodeAnalyzerSettings.getInstance().ERROR_STRIPE_MARK_MIN_HEIGHT);
276   }
277
278   private final List<Pair<NamedScope, NamedScopesHolder>> myScopes = ContainerUtil.createEmptyCOWList();
279   void reloadScopes() {
280     ApplicationManager.getApplication().assertIsDispatchThread();
281     List<Pair<NamedScope, NamedScopesHolder>> scopeList = new ArrayList<Pair<NamedScope, NamedScopesHolder>>();
282     addScopesToList(scopeList, NamedScopeManager.getInstance(myProject));
283     addScopesToList(scopeList, DependencyValidationManager.getInstance(myProject));
284     myScopes.clear();
285     myScopes.addAll(scopeList);
286   }
287
288   private static void addScopesToList(final List<Pair<NamedScope, NamedScopesHolder>> scopeList, final NamedScopesHolder holder) {
289     NamedScope[] scopes = holder.getScopes();
290     for (NamedScope scope : scopes) {
291       scopeList.add(Pair.create(scope, holder));
292     }
293   }
294
295   @NotNull
296   public List<Pair<NamedScope, NamedScopesHolder>> getScopeBasedHighlightingCachedScopes() {
297     return myScopes;
298   }
299
300   public void settingsChanged() {
301     DaemonCodeAnalyzerSettings settings = DaemonCodeAnalyzerSettings.getInstance();
302     if (settings.isCodeHighlightingChanged(myLastSettings)) {
303       restart();
304     }
305     myLastSettings = (DaemonCodeAnalyzerSettings)settings.clone();
306   }
307
308   public void updateVisibleHighlighters(@NotNull Editor editor) {
309     ApplicationManager.getApplication().assertIsDispatchThread();
310     // no need, will not work anyway
311   }
312
313   public void setUpdateByTimerEnabled(boolean value) {
314     myUpdateByTimerEnabled = value;
315     stopProcess(true);
316   }
317
318   public boolean isUpdateByTimerEnabled() {
319     return myUpdateByTimerEnabled;
320   }
321
322   public void setImportHintsEnabled(PsiFile file, boolean value) {
323     VirtualFile vFile = file.getVirtualFile();
324     if (value) {
325       myDisabledHintsFiles.remove(vFile);
326       stopProcess(true);
327     }
328     else {
329       myDisabledHintsFiles.add(vFile);
330       HintManager.getInstance().hideAllHints();
331     }
332   }
333
334   public void resetImportHintsEnabledForProject() {
335     myDisabledHintsFiles.clear();
336   }
337
338   public void setHighlightingEnabled(PsiFile file, boolean value) {
339     if (value) {
340       myDisabledHighlightingFiles.remove(file);
341     }
342     else {
343       myDisabledHighlightingFiles.add(file);
344     }
345   }
346
347   public boolean isHighlightingAvailable(PsiFile file) {
348     if (myDisabledHighlightingFiles.contains(file)) return false;
349
350     if (file == null || !file.isPhysical()) return false;
351     if (file instanceof PsiCompiledElement) return false;
352     final FileType fileType = file.getFileType();
353     if (fileType == StdFileTypes.GUI_DESIGNER_FORM){
354       return true;
355     }
356     // To enable T.O.D.O. highlighting
357     return !fileType.isBinary();
358   }
359
360   public boolean isImportHintsEnabled(PsiFile file) {
361     return isAutohintsAvailable(file) && !myDisabledHintsFiles.contains(file.getVirtualFile());
362   }
363
364   public boolean isAutohintsAvailable(PsiFile file) {
365     return isHighlightingAvailable(file) && !(file instanceof PsiCompiledElement);
366   }
367
368   public void restart() {
369     myFileStatusMap.markAllFilesDirty();
370     stopProcess(true);
371   }
372
373   public List<TextEditorHighlightingPass> getPassesToShowProgressFor(Document document) {
374     List<TextEditorHighlightingPass> allPasses = myPassExecutorService.getAllSubmittedPasses();
375     List<TextEditorHighlightingPass> result = new ArrayList<TextEditorHighlightingPass>(allPasses.size());
376     for (TextEditorHighlightingPass pass : allPasses) {
377       if (pass.getDocument() == document || pass.getDocument() == null) {
378         result.add(pass);
379       }
380     }
381     return result;
382   }
383
384   public boolean isAllAnalysisFinished(@NotNull PsiFile file) {
385     if (myDisposed) return false;
386     Document document = PsiDocumentManager.getInstance(myProject).getCachedDocument(file);
387     return document != null &&
388            document.getModificationStamp() == file.getModificationStamp() &&
389            myFileStatusMap.allDirtyScopesAreNull(document);
390   }
391
392   public boolean isErrorAnalyzingFinished(PsiFile file) {
393     if (myDisposed) return false;
394     Document document = PsiDocumentManager.getInstance(myProject).getCachedDocument(file);
395     return document != null &&
396            document.getModificationStamp() == file.getModificationStamp() &&
397            myFileStatusMap.getFileDirtyScope(document, Pass.UPDATE_ALL) == null;
398   }
399
400   public FileStatusMap getFileStatusMap() {
401     return myFileStatusMap;
402   }
403
404   public synchronized int getModificationCount() {
405     return myModificationCount;
406   }
407   
408   public synchronized boolean isRunning() {
409     return myUpdateProgress != null && !myUpdateProgress.isCanceled();
410   }
411
412   public synchronized void stopProcess(boolean toRestartAlarm) {
413     if (!allowToInterrupt) throw new RuntimeException("Cannot interrupt daemon");
414
415     cancelUpdateProgress(toRestartAlarm, "by Stop process");
416     myAlarm.cancelAllRequests();
417     boolean restart = toRestartAlarm && !myDisposed && myInitialized;
418     if (restart) {
419       myAlarm.addRequest(myUpdateRunnable, mySettings.AUTOREPARSE_DELAY);
420     }
421   }
422
423   private synchronized void cancelUpdateProgress(final boolean start, @NonNls String reason) {
424     PassExecutorService.log(myUpdateProgress, null, reason, start);
425     myModificationCount++;
426
427     if (myUpdateProgress != null) {
428       myUpdateProgress.cancel();
429       myPassExecutorService.cancelAll(false);
430       myUpdateProgress = null;
431     }
432   }
433
434   public static boolean processHighlights(@NotNull Document document,
435                                           @NotNull Project project,
436                                           @Nullable("null means all") final HighlightSeverity minSeverity,
437                                           final int startOffset,
438                                           final int endOffset,
439                                           @NotNull final Processor<HighlightInfo> processor) {
440     LOG.assertTrue(ApplicationManager.getApplication().isReadAccessAllowed());
441
442     final SeverityRegistrar severityRegistrar = SeverityRegistrar.getInstance(project);
443     MarkupModelEx model = (MarkupModelEx)((DocumentEx)document).getMarkupModel(project);
444     return model.processHighlightsOverlappingWith(startOffset, endOffset, new Processor<RangeHighlighterEx>() {
445       public boolean process(RangeHighlighterEx marker) {
446         Object tt = marker.getErrorStripeTooltip();
447         if (!(tt instanceof HighlightInfo)) return true;
448         HighlightInfo info = (HighlightInfo)tt;
449         return minSeverity != null && severityRegistrar.compare(info.getSeverity(), minSeverity) < 0 || processor.process(info);
450       }
451     });
452   }
453
454
455   public static boolean processHighlightsNearOffset(@NotNull Document document,
456                                                     @NotNull Project project,
457                                                     @NotNull final HighlightSeverity minSeverity,
458                                                     final int offset,
459                                                     final boolean includeFixRange,
460                                                     @NotNull final Processor<HighlightInfo> processor) {
461     return processHighlights(document, project, null, 0, document.getTextLength(), new Processor<HighlightInfo>() {
462       public boolean process(HighlightInfo info) {
463         if (!isOffsetInsideHighlightInfo(offset, info, includeFixRange)) return true;
464
465         int compare = info.getSeverity().compareTo(minSeverity);
466         if (compare < 0) {
467           return true;
468         }
469
470         return processor.process(info);
471       }
472     });
473   }
474
475   @Nullable
476   public HighlightInfo findHighlightByOffset(Document document, final int offset, final boolean includeFixRange) {
477     final List<HighlightInfo> foundInfoList = new SmartList<HighlightInfo>();
478     processHighlightsNearOffset(document, myProject, HighlightSeverity.INFORMATION, offset, includeFixRange, new Processor<HighlightInfo>() {
479       public boolean process(HighlightInfo info) {
480         if (!foundInfoList.isEmpty()) {
481           HighlightInfo foundInfo = foundInfoList.get(0);
482           int compare = foundInfo.getSeverity().compareTo(info.getSeverity());
483           if (compare < 0) {
484             foundInfoList.clear();
485           }
486           else if (compare > 0) {
487             return true;
488           }
489         }
490         foundInfoList.add(info);
491         return true;
492       }
493     });
494
495     if (foundInfoList.isEmpty()) return null;
496     if (foundInfoList.size() == 1) return foundInfoList.get(0);
497     return new HighlightInfoComposite(foundInfoList);
498   }
499
500   private static boolean isOffsetInsideHighlightInfo(int offset, HighlightInfo info, boolean includeFixRange) {
501     RangeHighlighterEx highlighter = info.highlighter;
502     if (highlighter == null || !highlighter.isValid()) return false;
503     int startOffset = highlighter.getStartOffset();
504     int endOffset = highlighter.getEndOffset();
505     if (startOffset > offset || offset > endOffset) {
506       if (!includeFixRange) return false;
507       if (info.fixMarker == null || !info.fixMarker.isValid()) return false;
508       startOffset = info.fixMarker.getStartOffset();
509       endOffset = info.fixMarker.getEndOffset();
510       if (startOffset > offset || offset > endOffset) return false;
511     }
512     return true;
513   }
514
515   static void addHighlight(MarkupModel markup,
516                            Project project,
517                            HighlightInfo toAdd) {
518     ApplicationManager.getApplication().assertIsDispatchThread();
519
520     stripWarningsCoveredByErrors(project, markup, toAdd);
521     
522     //DaemonCodeAnalyzer codeAnalyzer = DaemonCodeAnalyzer.getInstance(project);
523     //if (codeAnalyzer instanceof DaemonCodeAnalyzerImpl && ((DaemonCodeAnalyzerImpl)codeAnalyzer).myStatusBarUpdater != null) {
524     //  ((DaemonCodeAnalyzerImpl)codeAnalyzer).myStatusBarUpdater.updateStatus();
525     //}
526     
527   }
528
529   private static void stripWarningsCoveredByErrors(Project project, MarkupModel markup, final HighlightInfo toAdd) {
530     final SeverityRegistrar severityRegistrar = SeverityRegistrar.getInstance(project);
531     final Set<HighlightInfo> covered = new THashSet<HighlightInfo>();
532
533     // either toAdd is warning and covered by one of errors in highlightsToSet or toAdd is an error and covers warnings in highlightsToSet or it is OK
534     final boolean addingError = severityRegistrar.compare(HighlightSeverity.ERROR, toAdd.getSeverity()) <= 0;
535     boolean toAddIsVisible = processHighlights(markup.getDocument(), project, null, toAdd.getActualStartOffset(),
536                                                toAdd.getActualEndOffset(), new Processor<HighlightInfo>() {
537         public boolean process(HighlightInfo interval) {
538           boolean isError = severityRegistrar.compare(HighlightSeverity.ERROR, interval.getSeverity()) <= 0;
539           if (addingError && !isError && isCoveredBy(interval, toAdd)) {
540             covered.add(interval);
541           }
542           return addingError || !isError || !isCoveredBy(toAdd, interval);
543         }
544       });
545     if (toAddIsVisible) {
546       // ok
547       //highlightsToSet.add(toAdd);
548     }
549     else {
550       // toAdd is covered by
551       markup.removeHighlighter(toAdd.highlighter);
552     }
553     for (HighlightInfo warning : covered) {
554       RangeHighlighter highlighter = warning.highlighter;
555       if (highlighter != null) {
556         markup.removeHighlighter(highlighter);
557       }
558     }
559   }
560
561   //private static void stripWarningsCoveredByErrors(final Project project, IntervalTree<HighlightInfo> highlights, MarkupModel markup) {
562   //  final SeverityRegistrar severityRegistrar = SeverityRegistrar.getInstance(project);
563   //
564   //  final Set<HighlightInfo> currentErrors = new THashSet<HighlightInfo>();
565   //  final Set<HighlightInfo> covered = new THashSet<HighlightInfo>();
566   //
567   //
568   //  final PriorityQueue<HighlightInfo> ends = new PriorityQueue<HighlightInfo>(1, new Comparator<HighlightInfo>() {
569   //    public int compare(HighlightInfo o1, HighlightInfo o2) {
570   //      return o1.getActualEndOffset() - o2.getActualEndOffset();
571   //    }
572   //  });
573   //  highlights.process(new Processor<HighlightInfo>() {
574   //    public boolean process(HighlightInfo info) {
575   //      int startOffset = info.getActualStartOffset();
576   //
577   //      for (HighlightInfo nearestEnd = ends.peek(); nearestEnd != null && nearestEnd.getActualEndOffset() <= startOffset; nearestEnd = ends.peek()) {
578   //        currentErrors.remove(nearestEnd);
579   //        Interval t = ends.poll();
580   //        assert t == nearestEnd;
581   //      }
582   //
583   //      boolean isError = severityRegistrar.compare(HighlightSeverity.ERROR, info.getSeverity()) <= 0;
584   //      if (isError) {
585   //        currentErrors.add(info);
586   //      }
587   //      else {
588   //        if (info.getSeverity().myVal > 0) {
589   //          // warning, check for everlaps
590   //          for (HighlightInfo error : currentErrors) {
591   //            if (isCoveredBy(info, error)) {
592   //              covered.add(info);
593   //            }
594   //          }
595   //        }
596   //      }
597   //      ends.add(info);
598   //
599   //      return true;
600   //    }
601   //  });
602   //
603   //  for (HighlightInfo warning : covered) {
604   //    highlights.remove(warning);
605   //
606   //    RangeHighlighter highlighter = warning.highlighter;
607   //    if (highlighter != null) {
608   //      markup.removeHighlighter(highlighter);
609   //    }
610   //  }
611   //}
612
613   static boolean isCoveredBy(HighlightInfo info, HighlightInfo coveredBy) {
614     return coveredBy.startOffset <= info.startOffset && info.endOffset <= coveredBy.endOffset && info.getGutterIconRenderer() == null;
615   }
616
617   @Nullable
618   public static List<LineMarkerInfo> getLineMarkers(Document document, Project project) {
619     ApplicationManager.getApplication().assertIsDispatchThread();
620     MarkupModel markup = document.getMarkupModel(project);
621     return markup.getUserData(MARKERS_IN_EDITOR_DOCUMENT_KEY);
622   }
623
624   public static void setLineMarkers(@NotNull Document document, List<LineMarkerInfo> lineMarkers, Project project) {
625     ApplicationManager.getApplication().assertIsDispatchThread();
626     MarkupModel markup = document.getMarkupModel(project);
627     markup.putUserData(MARKERS_IN_EDITOR_DOCUMENT_KEY, lineMarkers);
628   }
629
630   public synchronized void setLastIntentionHint(Project project, PsiFile file, Editor editor, ShowIntentionsPass.IntentionsInfo intentions, boolean hasToRecreate) {
631     ApplicationManager.getApplication().assertIsDispatchThread();
632     hideLastIntentionHint();
633     IntentionHintComponent hintComponent = IntentionHintComponent.showIntentionHint(project, file, editor, intentions, false);
634     if (hasToRecreate) {
635       hintComponent.recreate();
636     }
637     myLastIntentionHint = hintComponent;
638   }
639
640   public synchronized void hideLastIntentionHint() {
641     if (myLastIntentionHint != null && myLastIntentionHint.isVisible()) {
642       myLastIntentionHint.hide();
643     }
644   }
645
646   public synchronized IntentionHintComponent getLastIntentionHint() {
647     return myLastIntentionHint;
648   }
649
650   public void writeExternal(Element parentNode) throws WriteExternalException {
651     Element disableHintsElement = new Element(DISABLE_HINTS_TAG);
652     parentNode.addContent(disableHintsElement);
653
654     List<String> array = new ArrayList<String>();
655     for (VirtualFile file : myDisabledHintsFiles) {
656       if (file.isValid()) {
657         array.add(file.getUrl());
658       }
659     }
660     Collections.sort(array);
661
662     for (String url : array) {
663       Element fileElement = new Element(FILE_TAG);
664       fileElement.setAttribute(URL_ATT, url);
665       disableHintsElement.addContent(fileElement);
666     }
667   }
668
669   public void readExternal(Element parentNode) throws InvalidDataException {
670     myDisabledHintsFiles.clear();
671
672     Element element = parentNode.getChild(DISABLE_HINTS_TAG);
673     if (element != null) {
674       for (Object o : element.getChildren(FILE_TAG)) {
675         Element e = (Element)o;
676
677         String url = e.getAttributeValue(URL_ATT);
678         if (url != null) {
679           VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(url);
680           if (file != null) {
681             myDisabledHintsFiles.add(file);
682           }
683         }
684       }
685     }
686   }
687
688   private Runnable createUpdateRunnable() {
689     return new Runnable() {
690       public void run() {
691         if (!myUpdateByTimerEnabled) return;
692         if (PowerSaveMode.isEnabled()) return;
693         if (myDisposed || !myProject.isInitialized()) return;
694         final Collection<FileEditor> activeEditors = myDaemonListeners.getSelectedEditors();
695         if (activeEditors.isEmpty()) return;
696         Map<FileEditor, HighlightingPass[]> passes = new THashMap<FileEditor, HighlightingPass[]>(activeEditors.size());
697         for (FileEditor fileEditor : activeEditors) {
698           BackgroundEditorHighlighter highlighter = fileEditor.getBackgroundHighlighter();
699           if (highlighter != null) {
700             HighlightingPass[] highlightingPasses = highlighter.createPassesForEditor();
701             passes.put(fileEditor, highlightingPasses);
702           }
703         }
704         // cancel all after calling createPasses() since there are perverts {@link com.intellij.util.xml.ui.DomUIFactoryImpl} who are changing PSI there
705         cancelUpdateProgress(true, "Cancel by alarm");
706         myAlarm.cancelAllRequests();
707         DaemonProgressIndicator progress;
708         synchronized (DaemonCodeAnalyzerImpl.this) {
709           DaemonProgressIndicator indicator = new DaemonProgressIndicator();
710           indicator.start();
711           myUpdateProgress = progress = indicator;
712         }
713         myPassExecutorService.submitPasses(passes, progress, Job.DEFAULT_PRIORITY);
714       }
715     };
716   }
717
718   public boolean canChangeFileSilently(PsiFileSystemItem file) {
719     return myDaemonListeners.canChangeFileSilently(file);
720   }
721
722   public void autoImportReferenceAtCursor(@NotNull Editor editor, @NotNull PsiFile file) {
723     for(ReferenceImporter importer: Extensions.getExtensions(ReferenceImporter.EP_NAME)) {
724       if (importer.autoImportReferenceAtCursor(editor, file)) break;
725     }
726   }
727
728   @NotNull
729   @TestOnly
730   public static List<HighlightInfo> getFileLevelHighlights(Project project,PsiFile file ) {
731     return UpdateHighlightersUtil.getFileLeveleHighlights(project, file);
732   }
733
734   @TestOnly
735   public void allowToInterrupt(boolean can) {
736     allowToInterrupt = can;
737   }
738 }