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
.analysis
;
19 import com
.intellij
.codeInsight
.daemon
.impl
.*;
20 import com
.intellij
.codeInsight
.highlighting
.HighlightErrorFilter
;
21 import com
.intellij
.lang
.Language
;
22 import com
.intellij
.lang
.LanguageAnnotators
;
23 import com
.intellij
.lang
.annotation
.Annotation
;
24 import com
.intellij
.lang
.annotation
.Annotator
;
25 import com
.intellij
.openapi
.extensions
.ExtensionPointListener
;
26 import com
.intellij
.openapi
.extensions
.ExtensionPointName
;
27 import com
.intellij
.openapi
.extensions
.Extensions
;
28 import com
.intellij
.openapi
.extensions
.PluginDescriptor
;
29 import com
.intellij
.openapi
.project
.DumbAware
;
30 import com
.intellij
.openapi
.project
.DumbService
;
31 import com
.intellij
.openapi
.project
.Project
;
32 import com
.intellij
.openapi
.util
.TextRange
;
33 import com
.intellij
.openapi
.util
.text
.StringUtil
;
34 import com
.intellij
.psi
.*;
35 import org
.jetbrains
.annotations
.NotNull
;
36 import org
.jetbrains
.annotations
.Nullable
;
38 import java
.util
.Collection
;
39 import java
.util
.List
;
44 public class DefaultHighlightVisitor
extends PsiElementVisitor
implements HighlightVisitor
, DumbAware
{
45 private final AnnotationHolderImpl myAnnotationHolder
= new AnnotationHolderImpl();
46 private HighlightInfoHolder myHolder
;
48 public static final ExtensionPointName
<HighlightErrorFilter
> FILTER_EP_NAME
= ExtensionPointName
.create("com.intellij.highlightErrorFilter");
49 private final HighlightErrorFilter
[] myErrorFilters
;
50 private final Project myProject
;
52 public DefaultHighlightVisitor(Project project
) {
54 myErrorFilters
= Extensions
.getExtensions(FILTER_EP_NAME
, project
);
57 public boolean suitableForFile(final PsiFile file
) {
61 public void visit(final PsiElement element
, final HighlightInfoHolder holder
) {
63 assert !myAnnotationHolder
.hasAnnotations() : myAnnotationHolder
;
67 public boolean analyze(final Runnable action
, final boolean updateWholeFile
, final PsiFile file
) {
72 myAnnotationHolder
.clear();
78 public HighlightVisitor
clone() {
79 return new DefaultHighlightVisitor(myProject
);
86 public void visitElement(final PsiElement element
) {
87 runAnnotators(element
);
90 private static final PerThreadMap
<Annotator
,Language
> cachedAnnotators
= new PerThreadMap
<Annotator
, Language
>() {
93 public Collection
<Annotator
> initialValue(@NotNull Language key
) {
94 return LanguageAnnotators
.INSTANCE
.allForLanguage(key
);
99 LanguageAnnotators
.INSTANCE
.addListener(new ExtensionPointListener
<Annotator
>() {
100 public void extensionAdded(Annotator extension
, @Nullable PluginDescriptor pluginDescriptor
) {
101 cachedAnnotators
.clear();
104 public void extensionRemoved(Annotator extension
, @Nullable PluginDescriptor pluginDescriptor
) {
105 cachedAnnotators
.clear();
109 //final ExtensionPoint<LanguageExtensionPoint<Annotator>> point = Extensions.getRootArea().getExtensionPoint(LanguageAnnotators.EP_NAME);
110 //point.addExtensionPointListener(new ExtensionPointAndAreaListener<LanguageExtensionPoint<Annotator>>() {
111 // public void areaReplaced(ExtensionsArea area) {
112 // cachedAnnotators.clear();
115 // public void extensionAdded(LanguageExtensionPoint<Annotator> extension, @Nullable PluginDescriptor pluginDescriptor) {
116 // cachedAnnotators.clear();
119 // public void extensionRemoved(LanguageExtensionPoint<Annotator> extension, @Nullable PluginDescriptor pluginDescriptor) {
120 // cachedAnnotators.clear();
125 private void runAnnotators(final PsiElement element
) {
126 List
<Annotator
> annotators
= cachedAnnotators
.get(element
.getLanguage());
127 if (!annotators
.isEmpty()) {
128 final boolean dumb
= DumbService
.getInstance(myProject
).isDumb();
129 //noinspection ForLoopReplaceableByForEach
130 for (int i
= 0; i
< annotators
.size(); i
++) {
131 Annotator annotator
= annotators
.get(i
);
132 if (dumb
&& !(annotator
instanceof DumbAware
)) {
136 annotator
.annotate(element
, myAnnotationHolder
);
138 if (myAnnotationHolder
.hasAnnotations()) {
139 for (Annotation annotation
: myAnnotationHolder
) {
140 myHolder
.add(HighlightInfo
.fromAnnotation(annotation
));
142 myAnnotationHolder
.clear();
147 public void visitErrorElement(final PsiErrorElement element
) {
148 for(HighlightErrorFilter errorFilter
: myErrorFilters
) {
149 if (!errorFilter
.shouldHighlightErrorElement(element
)) return;
152 HighlightInfo info
= createErrorElementInfo(element
);
156 public static HighlightInfo
createErrorElementInfo(final PsiErrorElement element
) {
157 TextRange range
= element
.getTextRange();
158 if (!range
.isEmpty()) {
159 final HighlightInfo highlightInfo
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, range
, element
.getErrorDescription());
160 for(ErrorQuickFixProvider provider
: Extensions
.getExtensions(ErrorQuickFixProvider
.EP_NAME
)) {
161 provider
.registerErrorQuickFix(element
, highlightInfo
);
163 return highlightInfo
;
165 int offset
= range
.getStartOffset();
166 PsiFile containingFile
= element
.getContainingFile();
167 int fileLength
= containingFile
.getTextLength();
168 FileViewProvider viewProvider
= containingFile
.getViewProvider();
169 PsiElement elementAtOffset
= viewProvider
.findElementAt(offset
, viewProvider
.getBaseLanguage());
170 String text
= elementAtOffset
== null ?
null : elementAtOffset
.getText();
172 if (offset
< fileLength
&& text
!= null && !StringUtil
.startsWithChar(text
, '\n') && !StringUtil
.startsWithChar(text
, '\r')) {
173 info
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, offset
, offset
+ 1, element
.getErrorDescription());
179 start
= offset
/* - 1*/;
184 end
= offset
< fileLength ? offset
+ 1 : offset
;
186 info
= HighlightInfo
.createHighlightInfo(HighlightInfoType
.ERROR
, element
, start
, end
, element
.getErrorDescription(),element
.getErrorDescription(), true, null);