2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com
.intellij
.codeInsight
.daemon
.impl
;
19 import com
.intellij
.codeHighlighting
.Pass
;
20 import com
.intellij
.codeHighlighting
.TextEditorHighlightingPass
;
21 import com
.intellij
.codeInsight
.daemon
.DaemonBundle
;
22 import com
.intellij
.codeInsight
.daemon
.DaemonCodeAnalyzer
;
23 import com
.intellij
.codeInsight
.daemon
.impl
.analysis
.HighlightInfoHolder
;
24 import com
.intellij
.codeInsight
.daemon
.impl
.analysis
.HighlightLevelUtil
;
25 import com
.intellij
.codeInsight
.problems
.ProblemImpl
;
26 import com
.intellij
.concurrency
.JobUtil
;
27 import com
.intellij
.injected
.editor
.DocumentWindow
;
28 import com
.intellij
.lang
.Language
;
29 import com
.intellij
.lang
.annotation
.HighlightSeverity
;
30 import com
.intellij
.lang
.injection
.InjectedLanguageManager
;
31 import com
.intellij
.openapi
.application
.ApplicationManager
;
32 import com
.intellij
.openapi
.diagnostic
.Logger
;
33 import com
.intellij
.openapi
.editor
.Document
;
34 import com
.intellij
.openapi
.editor
.HighlighterColors
;
35 import com
.intellij
.openapi
.editor
.RangeMarker
;
36 import com
.intellij
.openapi
.editor
.colors
.EditorColors
;
37 import com
.intellij
.openapi
.editor
.colors
.EditorColorsManager
;
38 import com
.intellij
.openapi
.editor
.colors
.EditorColorsScheme
;
39 import com
.intellij
.openapi
.editor
.colors
.TextAttributesKey
;
40 import com
.intellij
.openapi
.editor
.markup
.TextAttributes
;
41 import com
.intellij
.openapi
.extensions
.Extensions
;
42 import com
.intellij
.openapi
.fileTypes
.SyntaxHighlighter
;
43 import com
.intellij
.openapi
.fileTypes
.SyntaxHighlighterFactory
;
44 import com
.intellij
.openapi
.progress
.ProcessCanceledException
;
45 import com
.intellij
.openapi
.progress
.ProgressIndicator
;
46 import com
.intellij
.openapi
.progress
.ProgressManager
;
47 import com
.intellij
.openapi
.project
.DumbAware
;
48 import com
.intellij
.openapi
.project
.DumbService
;
49 import com
.intellij
.openapi
.project
.Project
;
50 import com
.intellij
.openapi
.util
.*;
51 import com
.intellij
.openapi
.vfs
.VirtualFile
;
52 import com
.intellij
.problems
.Problem
;
53 import com
.intellij
.problems
.WolfTheProblemSolver
;
54 import com
.intellij
.psi
.*;
55 import com
.intellij
.psi
.impl
.source
.tree
.injected
.InjectedLanguageUtil
;
56 import com
.intellij
.psi
.search
.PsiSearchHelper
;
57 import com
.intellij
.psi
.search
.TodoItem
;
58 import com
.intellij
.psi
.tree
.IElementType
;
59 import com
.intellij
.util
.Processor
;
60 import com
.intellij
.util
.SmartList
;
61 import gnu
.trove
.THashSet
;
62 import org
.jetbrains
.annotations
.NotNull
;
67 import java
.util
.List
;
68 import java
.util
.concurrent
.atomic
.AtomicInteger
;
70 public class GeneralHighlightingPass
extends ProgressableTextEditorHighlightingPass
implements DumbAware
{
71 private static final Logger LOG
= Logger
.getInstance("#com.intellij.codeInsight.daemon.impl.GeneralHighlightingPass");
72 static final Icon IN_PROGRESS_ICON
= IconLoader
.getIcon("/general/errorsInProgress.png");
73 static final String PRESENTABLE_NAME
= DaemonBundle
.message("pass.syntax");
74 private static final Key
<Boolean
> HAS_ERROR_ELEMENT
= Key
.create("HAS_ERROR_ELEMENT");
76 private final int myStartOffset
;
77 private final int myEndOffset
;
78 private final boolean myUpdateAll
;
80 private volatile Collection
<HighlightInfo
> myHighlights
= Collections
.emptyList();
81 private final Map
<TextRange
,Collection
<HighlightInfo
>> myInjectedPsiHighlights
= new HashMap
<TextRange
, Collection
<HighlightInfo
>>();
83 protected volatile boolean myHasErrorElement
;
84 private volatile boolean myErrorFound
;
85 private static final Comparator
<HighlightVisitor
> VISITOR_ORDER_COMPARATOR
= new Comparator
<HighlightVisitor
>() {
86 public int compare(final HighlightVisitor o1
, final HighlightVisitor o2
) {
87 return o1
.order() - o2
.order();
90 private volatile UserDataHolder myProgress
;
92 public GeneralHighlightingPass(@NotNull Project project
,
93 @NotNull PsiFile file
,
94 @NotNull Document document
,
98 super(project
, document
, IN_PROGRESS_ICON
, PRESENTABLE_NAME
, file
, true);
99 myStartOffset
= startOffset
;
100 myEndOffset
= endOffset
;
101 myUpdateAll
= updateAll
;
103 LOG
.assertTrue(file
.isValid());
104 setId(Pass
.UPDATE_ALL
);
105 myHasErrorElement
= !isWholeFileHighlighting() && Boolean
.TRUE
.equals(myFile
.getUserData(HAS_ERROR_ELEMENT
));
106 FileStatusMap fileStatusMap
= ((DaemonCodeAnalyzerImpl
)DaemonCodeAnalyzer
.getInstance(myProject
)).getFileStatusMap();
107 myErrorFound
= !isWholeFileHighlighting() && fileStatusMap
.wasErrorFound(myDocument
);
110 private static final Key
<AtomicInteger
> HIGHLIGHT_VISITOR_INSTANCE_COUNT
= new Key
<AtomicInteger
>("HIGHLIGHT_VISITOR_INSTANCE_COUNT");
112 private HighlightVisitor
[] createHighlightVisitors() {
113 int oldCount
= incVisitorUsageCount(1);
114 HighlightVisitor
[] highlightVisitors
= Extensions
.getExtensions(HighlightVisitor
.EP_HIGHLIGHT_VISITOR
, myProject
);
116 HighlightVisitor
[] clones
= new HighlightVisitor
[highlightVisitors
.length
];
117 for (int i
= 0; i
< highlightVisitors
.length
; i
++) {
118 HighlightVisitor highlightVisitor
= highlightVisitors
[i
];
119 clones
[i
] = highlightVisitor
.clone();
121 highlightVisitors
= clones
;
123 return highlightVisitors
;
127 private int incVisitorUsageCount(int delta
) {
128 AtomicInteger count
= myProject
.getUserData(HIGHLIGHT_VISITOR_INSTANCE_COUNT
);
130 count
= ((UserDataHolderEx
)myProject
).putUserDataIfAbsent(HIGHLIGHT_VISITOR_INSTANCE_COUNT
, new AtomicInteger(0));
132 int old
= count
.getAndAdd(delta
);
133 assert old
+ delta
>= 0 : old
+";" + delta
;
137 protected void collectInformationWithProgress(final ProgressIndicator progress
) {
138 myProgress
= (UserDataHolder
)progress
;
139 final Collection
<HighlightInfo
> result
= new THashSet
<HighlightInfo
>(100);
140 DaemonCodeAnalyzer daemonCodeAnalyzer
= DaemonCodeAnalyzer
.getInstance(myProject
);
141 FileStatusMap fileStatusMap
= ((DaemonCodeAnalyzerImpl
)daemonCodeAnalyzer
).getFileStatusMap();
142 HighlightVisitor
[] highlightVisitors
= createHighlightVisitors();
143 HighlightVisitor
[] filtered
= filterVisitors(highlightVisitors
, myFile
);
145 final FileViewProvider viewProvider
= myFile
.getViewProvider();
146 final Set
<Language
> relevantLanguages
= viewProvider
.getLanguages();
147 List
<PsiElement
> elements
= null;
148 for (Language language
: relevantLanguages
) {
149 PsiElement psiRoot
= viewProvider
.getPsi(language
);
150 if (!HighlightLevelUtil
.shouldHighlight(psiRoot
)) continue;
151 List
<PsiElement
> underRoot
= CollectHighlightsUtil
.getElementsInRange(psiRoot
, myStartOffset
, myEndOffset
);
152 if (elements
== null) {
153 elements
= underRoot
;
156 elements
.addAll(underRoot
);
158 if (underRoot
.isEmpty()) {
159 elements
.add(psiRoot
);
162 if (elements
!= null) {
163 result
.addAll(collectHighlights(elements
, progress
, filtered
));
164 addInjectedPsiHighlights(elements
);
168 result
.addAll(highlightTodos(myFile
, myDocument
.getCharsSequence(), myStartOffset
, myEndOffset
));
172 fileStatusMap
.setErrorFoundFlag(myDocument
, myErrorFound
);
176 incVisitorUsageCount(-1);
178 myHighlights
= result
;
181 private void addInjectedPsiHighlights(@NotNull final List
<PsiElement
> elements
) {
182 List
<DocumentWindow
> injected
= InjectedLanguageUtil
.getCachedInjectedDocuments(myFile
);
183 Collection
<PsiElement
> hosts
= new THashSet
<PsiElement
>(elements
.size() + injected
.size());
185 // rehighlight all injected PSI regardless the range,
186 // since change in one place can lead to invalidation of injected PSI in (completely) other place.
187 for (DocumentWindow documentRange
: injected
) {
188 if (!documentRange
.isValid()) continue;
189 PsiFile file
= PsiDocumentManager
.getInstance(myProject
).getPsiFile(documentRange
);
190 if (file
== null) continue;
191 PsiElement context
= file
.getContext();
194 && !file
.getProject().isDisposed()
195 && (myUpdateAll
|| new ProperTextRange(myStartOffset
, myEndOffset
).intersects(context
.getTextRange()))) {
199 hosts
.addAll(elements
);
201 final Collection
<PsiFile
> injectedFiles
= new THashSet
<PsiFile
>();
202 EditorColorsScheme scheme
= EditorColorsManager
.getInstance().getGlobalScheme();
203 final TextAttributes injectedAttributes
= scheme
.getAttributes(EditorColors
.INJECTED_LANGUAGE_FRAGMENT
);
205 for (PsiElement element
: hosts
) {
206 InjectedLanguageUtil
.enumerate(element
, myFile
, new PsiLanguageInjectionHost
.InjectedPsiVisitor() {
207 public void visit(@NotNull PsiFile injectedPsi
, @NotNull List
<PsiLanguageInjectionHost
.Shred
> places
) {
208 if (injectedFiles
.add(injectedPsi
)) { // for concatenations there can be many injection hosts with only one injected PSI
209 for (PsiLanguageInjectionHost
.Shred place
: places
) {
210 TextRange textRange
= place
.getRangeInsideHost().shiftRight(place
.host
.getTextRange().getStartOffset());
211 if (textRange
.isEmpty()) continue;
212 String desc
= injectedPsi
.getText();
213 HighlightInfo info
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.INJECTED_LANGUAGE_FRAGMENT
, textRange
, null, desc
, injectedAttributes
);
214 addHighlightInfo(textRange
, info
);
220 if (injectedFiles
.isEmpty()) return;
221 final InjectedLanguageManager injectedLanguageManager
= InjectedLanguageManager
.getInstance(myProject
);
223 JobUtil
.invokeConcurrentlyUnderMyProgress(injectedFiles
, new Processor
<PsiFile
>() {
224 public boolean process(final PsiFile injectedPsi
) {
225 DocumentWindow documentWindow
= (DocumentWindow
)PsiDocumentManager
.getInstance(myProject
).getCachedDocument(injectedPsi
);
226 HighlightInfoHolder holder
= createInfoHolder(injectedPsi
);
227 runHighlightVisitosForInjected(injectedPsi
, holder
);
228 for (int i
=0; i
<holder
.size();i
++) {
229 HighlightInfo info
= holder
.get(i
);
230 final int startOffset
= documentWindow
.injectedToHost(info
.startOffset
);
231 final TextRange fixedTextRange
= getFixedTextRange(documentWindow
, startOffset
);
232 addPatchedInfos(info
, injectedPsi
, documentWindow
, injectedLanguageManager
, fixedTextRange
);
235 highlightInjectedSyntax(injectedPsi
, holder
);
236 for (int i
=0; i
<holder
.size();i
++) {
237 HighlightInfo info
= holder
.get(i
);
238 final int startOffset
= info
.startOffset
;
239 final TextRange fixedTextRange
= getFixedTextRange(documentWindow
, startOffset
);
240 if (fixedTextRange
== null) {
241 addHighlightInfo(new TextRange(info
.startOffset
, info
.endOffset
), info
);
244 HighlightInfo patched
=
245 new HighlightInfo(info
.forcedTextAttributes
, info
.type
, fixedTextRange
.getStartOffset(), fixedTextRange
.getEndOffset(),
246 info
.description
, info
.toolTip
, info
.type
.getSeverity(null), info
.isAfterEndOfLine
, null, false);
247 addHighlightInfo(fixedTextRange
, patched
);
252 Collection
<HighlightInfo
> todos
= highlightTodos(injectedPsi
, injectedPsi
.getText(), 0, injectedPsi
.getTextLength());
253 for (HighlightInfo info
: todos
) {
254 addPatchedInfos(info
, injectedPsi
, documentWindow
, injectedLanguageManager
, null);
259 }, "Highlight injected language fragments");
262 private static TextRange
getFixedTextRange(@NotNull DocumentWindow documentWindow
, int startOffset
) {
263 final TextRange fixedTextRange
;
264 TextRange textRange
= documentWindow
.getHostRange(startOffset
);
265 if (textRange
== null) {
266 // todo[cdr] check this fix. prefix/suffix code annotation case
267 textRange
= findNearestTextRange(documentWindow
, startOffset
);
268 final boolean isBefore
= startOffset
< textRange
.getStartOffset();
269 fixedTextRange
= new ProperTextRange(isBefore ? textRange
.getStartOffset() - 1 : textRange
.getEndOffset(),
270 isBefore ? textRange
.getStartOffset() : textRange
.getEndOffset() + 1);
273 fixedTextRange
= null;
275 return fixedTextRange
;
278 private void addPatchedInfos(HighlightInfo info
, PsiFile injectedPsi
, DocumentWindow documentWindow
, InjectedLanguageManager injectedLanguageManager
,
279 TextRange fixedTextRange
) {
280 ProperTextRange textRange
= new ProperTextRange(info
.startOffset
, info
.endOffset
);
281 List
<TextRange
> editables
= injectedLanguageManager
.intersectWithAllEditableFragments(injectedPsi
, textRange
);
282 for (TextRange editable
: editables
) {
283 TextRange hostRange
= fixedTextRange
== null ? documentWindow
.injectedToHost(editable
) : fixedTextRange
;
285 HighlightInfo patched
=
286 new HighlightInfo(info
.forcedTextAttributes
, info
.type
, hostRange
.getStartOffset(), hostRange
.getEndOffset(),
287 info
.description
, info
.toolTip
, info
.type
.getSeverity(null), info
.isAfterEndOfLine
, null, false);
289 if (info
.quickFixActionRanges
!= null) {
290 for (Pair
<HighlightInfo
.IntentionActionDescriptor
, TextRange
> pair
: info
.quickFixActionRanges
) {
291 TextRange quickfixTextRange
= pair
.getSecond();
292 List
<TextRange
> editableQF
= injectedLanguageManager
.intersectWithAllEditableFragments(injectedPsi
, quickfixTextRange
);
293 for (TextRange editableRange
: editableQF
) {
294 HighlightInfo
.IntentionActionDescriptor descriptor
= pair
.getFirst();
295 if (patched
.quickFixActionRanges
== null) patched
.quickFixActionRanges
= new ArrayList
<Pair
<HighlightInfo
.IntentionActionDescriptor
, TextRange
>>();
296 TextRange hostEditableRange
= documentWindow
.injectedToHost(editableRange
);
297 patched
.quickFixActionRanges
.add(Pair
.create(descriptor
, hostEditableRange
));
301 addHighlightInfo(hostRange
, patched
);
305 private void addHighlightInfo(@NotNull TextRange textRange
, @NotNull HighlightInfo highlightInfo
) {
306 synchronized (myInjectedPsiHighlights
) {
307 Collection
<HighlightInfo
> infos
= myInjectedPsiHighlights
.get(textRange
);
309 infos
= new SmartList
<HighlightInfo
>();
310 myInjectedPsiHighlights
.put(textRange
, infos
);
312 infos
.add(highlightInfo
);
316 // finds the first nearest text range
317 private static TextRange
findNearestTextRange(final DocumentWindow documentWindow
, final int startOffset
) {
318 TextRange textRange
= null;
319 for (RangeMarker marker
: documentWindow
.getHostRanges()) {
320 TextRange curRange
= InjectedLanguageUtil
.toTextRange(marker
);
321 if (curRange
.getStartOffset() > startOffset
&& textRange
!= null) break;
322 textRange
= curRange
;
324 assert textRange
!= null;
328 private void runHighlightVisitosForInjected(final PsiFile injectedPsi
, final HighlightInfoHolder holder
) {
329 HighlightVisitor
[] visitors
= createHighlightVisitors();
331 HighlightVisitor
[] filtered
= filterVisitors(visitors
, injectedPsi
);
332 final List
<PsiElement
> elements
= CollectHighlightsUtil
.getElementsInRange(injectedPsi
, 0, injectedPsi
.getTextLength());
333 for (final HighlightVisitor hvisitor
: filtered
) {
334 hvisitor
.analyze(new Runnable() {
336 for (PsiElement element
: elements
) {
337 ProgressManager
.checkCanceled();
338 hvisitor
.visit(element
, holder
);
341 }, true, injectedPsi
);
345 incVisitorUsageCount(-1);
349 private static void highlightInjectedSyntax(final PsiFile injectedPsi
, HighlightInfoHolder holder
) {
350 List
<Trinity
<IElementType
, PsiLanguageInjectionHost
, TextRange
>> tokens
= InjectedLanguageUtil
.getHighlightTokens(injectedPsi
);
351 if (tokens
== null) return;
353 final Language injectedLanguage
= injectedPsi
.getLanguage();
354 SyntaxHighlighter syntaxHighlighter
=
355 SyntaxHighlighterFactory
.getSyntaxHighlighter(injectedLanguage
, injectedPsi
.getProject(), injectedPsi
.getVirtualFile());
356 EditorColorsScheme globalScheme
= EditorColorsManager
.getInstance().getGlobalScheme();
357 final TextAttributes defaultAttrs
= globalScheme
.getAttributes(HighlighterColors
.TEXT
);
359 for (Trinity
<IElementType
, PsiLanguageInjectionHost
, TextRange
> token
: tokens
) {
360 IElementType tokenType
= token
.getFirst();
361 PsiLanguageInjectionHost injectionHost
= token
.getSecond();
362 TextRange textRange
= token
.getThird();
363 TextAttributesKey
[] keys
= syntaxHighlighter
.getTokenHighlights(tokenType
);
364 if (textRange
.getLength() == 0) continue;
366 TextRange annRange
= textRange
.shiftRight(injectionHost
.getTextRange().getStartOffset());
367 // force attribute colors to override host' ones
368 TextAttributes attributes
= null;
369 for(TextAttributesKey key
:keys
) {
370 TextAttributes attrs2
= globalScheme
.getAttributes(key
);
371 if (attrs2
!= null) {
372 attributes
= attributes
!= null ? TextAttributes
.merge(attributes
, attrs2
):attrs2
;
375 TextAttributes forcedAttributes
;
376 if (attributes
== null || attributes
.isEmpty() || attributes
.equals(defaultAttrs
)) {
377 forcedAttributes
= TextAttributes
.ERASE_MARKER
;
380 Color back
= attributes
.getBackgroundColor() == null ? globalScheme
.getDefaultBackground() : attributes
.getBackgroundColor();
381 Color fore
= attributes
.getForegroundColor() == null ? globalScheme
.getDefaultForeground() : attributes
.getForegroundColor();
382 forcedAttributes
= new TextAttributes(fore
, back
, attributes
.getEffectColor(), attributes
.getEffectType(), attributes
.getFontType());
385 HighlightInfo info
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.INJECTED_LANGUAGE_FRAGMENT
, annRange
, null,null,forcedAttributes
);
390 private boolean isWholeFileHighlighting() {
391 return myUpdateAll
&& myStartOffset
== 0 && myEndOffset
== myFile
.getTextLength();
394 private static final Key
<List
<GeneralHighlightingPass
>> UPDATE_ALL_FINISHED
= Key
.create("UPDATE_ALL_FINISHED");
395 protected void applyInformationWithProgress() {
396 myFile
.putUserData(HAS_ERROR_ELEMENT
, myHasErrorElement
);
398 // highlights from both passes should be in the same layer
399 TextRange range
= new TextRange(myStartOffset
, myEndOffset
);
400 Collection
<HighlightInfo
> collection
= myInjectedPsiHighlights
.get(range
);
401 if (collection
== null) {
402 collection
= new ArrayList
<HighlightInfo
>(myHighlights
.size());
404 collection
.addAll(myHighlights
);
405 myInjectedPsiHighlights
.put(range
, collection
);
406 myHighlights
= Collections
.emptyList();
408 List
<GeneralHighlightingPass
> updateAllFinished
= myProgress
.getUserData(UPDATE_ALL_FINISHED
);
409 if (updateAllFinished
== null || !updateAllFinished
.contains(this)) {
410 // prevent situation when visible pass finished after updateAll pass and tries to wipe out its results
411 UpdateHighlightersUtil
.setHighlightersToEditor(myProject
, myDocument
, myInjectedPsiHighlights
, Pass
.UPDATE_ALL
);
414 if (updateAllFinished
== null) {
415 updateAllFinished
= new ArrayList
<GeneralHighlightingPass
>();
416 myProgress
.putUserData(UPDATE_ALL_FINISHED
, updateAllFinished
);
418 updateAllFinished
.add(this);
423 reportErrorsToWolf();
428 public Collection
<HighlightInfo
> getHighlights() {
429 ArrayList
<HighlightInfo
> list
= new ArrayList
<HighlightInfo
>(myHighlights
);
430 for (Collection
<HighlightInfo
> infos
: myInjectedPsiHighlights
.values()) {
436 private Collection
<HighlightInfo
> collectHighlights(@NotNull final List
<PsiElement
> elements
, @NotNull final ProgressIndicator progress
,
437 final HighlightVisitor
[] visitors
) {
438 final Set
<PsiElement
> skipParentsSet
= new THashSet
<PsiElement
>();
439 final Set
<HighlightInfo
> gotHighlights
= new THashSet
<HighlightInfo
>();
441 final boolean forceHighlightParents
= forceHighlightParents();
443 final HighlightInfoHolder holder
= createInfoHolder(myFile
);
444 setProgressLimit((long)elements
.size() * visitors
.length
);
446 final int chunkSize
= Math
.max(1, elements
.size() / 100); // one percent precision is enough
447 for (final HighlightVisitor visitor
: visitors
) {
448 Runnable action
= new Runnable() {
450 int nextLimit
= chunkSize
;
451 for (int i
= 0; i
< elements
.size(); i
++) {
452 PsiElement element
= elements
.get(i
);
453 ProgressManager
.checkCanceled();
455 if (element
!= myFile
&& !skipParentsSet
.isEmpty() && element
.getFirstChild() != null && skipParentsSet
.contains(element
)) {
456 skipParentsSet
.add(element
.getParent());
460 if (element
instanceof PsiErrorElement
) {
461 myHasErrorElement
= true;
465 visitor
.visit(element
, holder
);
466 if (i
== nextLimit
) {
467 advanceProgress(chunkSize
);
468 nextLimit
= i
+ chunkSize
;
471 //noinspection ForLoopReplaceableByForEach
472 for (int j
= 0; j
< holder
.size(); j
++) {
473 HighlightInfo info
= holder
.get(j
);
475 // have to filter out already obtained highlights
476 if (!gotHighlights
.add(info
)) continue;
477 boolean isError
= info
.getSeverity() == HighlightSeverity
.ERROR
;
479 if (!forceHighlightParents
) {
480 skipParentsSet
.add(element
.getParent());
486 advanceProgress(elements
.size() - (nextLimit
-chunkSize
));
489 if (!visitor
.analyze(action
, myUpdateAll
, myFile
)) {
490 cancelAndRestartDaemonLater(progress
, myProject
, this);
494 return gotHighlights
;
497 private static HighlightVisitor
[] filterVisitors(HighlightVisitor
[] highlightVisitors
, final PsiFile file
) {
498 final List
<HighlightVisitor
> visitors
= new ArrayList
<HighlightVisitor
>(highlightVisitors
.length
);
499 List
<HighlightVisitor
> list
= Arrays
.asList(highlightVisitors
);
500 for (HighlightVisitor visitor
: DumbService
.getInstance(file
.getProject()).filterByDumbAwareness(list
)) {
501 if (visitor
.suitableForFile(file
)) visitors
.add(visitor
);
503 LOG
.assertTrue(!visitors
.isEmpty(), list
);
505 HighlightVisitor
[] visitorArray
= visitors
.toArray(new HighlightVisitor
[visitors
.size()]);
506 Arrays
.sort(visitorArray
, VISITOR_ORDER_COMPARATOR
);
510 static Void
cancelAndRestartDaemonLater(ProgressIndicator progress
, final Project project
, TextEditorHighlightingPass pass
) {
511 PassExecutorService
.log(progress
, pass
, "Cancel and restart");
513 ApplicationManager
.getApplication().invokeLater(new Runnable() {
516 Thread
.sleep(new Random().nextInt(100));
518 catch (InterruptedException e
) {
521 DaemonCodeAnalyzer
.getInstance(project
).restart();
523 }, project
.getDisposed());
524 throw new ProcessCanceledException();
527 private boolean forceHighlightParents() {
528 boolean forceHighlightParents
= false;
529 for(HighlightRangeExtension extension
: Extensions
.getExtensions(HighlightRangeExtension
.EP_NAME
)) {
530 if (extension
.isForceHighlightParents(myFile
)) {
531 forceHighlightParents
= true;
535 return forceHighlightParents
;
538 protected HighlightInfoHolder
createInfoHolder(final PsiFile file
) {
539 final HighlightInfoFilter
[] filters
= ApplicationManager
.getApplication().getExtensions(HighlightInfoFilter
.EXTENSION_POINT_NAME
);
540 return new HighlightInfoHolder(file
, filters
);
543 private static Collection
<HighlightInfo
> highlightTodos(PsiFile file
, CharSequence text
, int startOffset
, int endOffset
) {
544 PsiManager psiManager
= file
.getManager();
545 PsiSearchHelper helper
= psiManager
.getSearchHelper();
546 TodoItem
[] todoItems
= helper
.findTodoItems(file
, startOffset
, endOffset
);
547 if (todoItems
.length
== 0) return Collections
.emptyList();
549 List
<HighlightInfo
> list
= new ArrayList
<HighlightInfo
>(todoItems
.length
);
550 for (TodoItem todoItem
: todoItems
) {
551 TextRange range
= todoItem
.getTextRange();
552 String description
= text
.subSequence(range
.getStartOffset(), range
.getEndOffset()).toString();
553 TextAttributes attributes
= todoItem
.getPattern().getAttributes().getTextAttributes();
554 HighlightInfo info
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.TODO
, range
, description
, description
, attributes
);
561 private void reportErrorsToWolf() {
562 if (!myFile
.getViewProvider().isPhysical()) return; // e.g. errors in evaluate expression
563 Project project
= myFile
.getProject();
564 if (!PsiManager
.getInstance(project
).isInProject(myFile
)) return; // do not report problems in libraries
565 VirtualFile file
= myFile
.getVirtualFile();
566 if (file
== null) return;
568 List
<Problem
> problems
= convertToProblems(getHighlights(), file
, myHasErrorElement
);
569 WolfTheProblemSolver wolf
= WolfTheProblemSolver
.getInstance(project
);
571 List
<HighlightInfo
> errors
= DaemonCodeAnalyzerImpl
.getHighlights(getDocument(), HighlightSeverity
.ERROR
, project
);
572 if (errors
.isEmpty() || isWholeFileHighlighting()) {
573 wolf
.reportProblems(file
, problems
);
576 wolf
.weHaveGotProblems(file
, problems
);
580 public double getProgress() {
581 // do not show progress of visible highlighters update
582 return myUpdateAll ?
super.getProgress() : -1;
585 private static List
<Problem
> convertToProblems(final Collection
<HighlightInfo
> infos
, final VirtualFile file
,
586 final boolean hasErrorElement
) {
587 List
<Problem
> problems
= new SmartList
<Problem
>();
588 for (HighlightInfo info
: infos
) {
589 if (info
.getSeverity() == HighlightSeverity
.ERROR
) {
590 Problem problem
= new ProblemImpl(file
, info
, hasErrorElement
);
591 problems
.add(problem
);
598 public String
toString() {
599 return super.toString() + " updateAll="+myUpdateAll
+" range=("+myStartOffset
+","+myEndOffset
+")";