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
.codeInsight
.daemon
.HighlightDisplayKey
;
20 import com
.intellij
.codeInsight
.daemon
.impl
.quickfix
.QuickFixAction
;
21 import com
.intellij
.codeInsight
.intention
.IntentionAction
;
22 import com
.intellij
.codeInsight
.intention
.IntentionManager
;
23 import com
.intellij
.codeInspection
.*;
24 import com
.intellij
.codeInspection
.actions
.CleanupInspectionIntention
;
25 import com
.intellij
.codeInspection
.ex
.LocalInspectionToolWrapper
;
26 import com
.intellij
.codeInspection
.ex
.QuickFixWrapper
;
27 import com
.intellij
.lang
.ASTNode
;
28 import com
.intellij
.lang
.annotation
.Annotation
;
29 import com
.intellij
.lang
.annotation
.HighlightSeverity
;
30 import com
.intellij
.openapi
.application
.ApplicationManager
;
31 import com
.intellij
.openapi
.diagnostic
.Logger
;
32 import com
.intellij
.openapi
.editor
.RangeMarker
;
33 import com
.intellij
.openapi
.editor
.colors
.CodeInsightColors
;
34 import com
.intellij
.openapi
.editor
.colors
.EditorColorsManager
;
35 import com
.intellij
.openapi
.editor
.colors
.EditorColorsScheme
;
36 import com
.intellij
.openapi
.editor
.colors
.TextAttributesKey
;
37 import com
.intellij
.openapi
.editor
.ex
.RangeHighlighterEx
;
38 import com
.intellij
.openapi
.editor
.markup
.GutterIconRenderer
;
39 import com
.intellij
.openapi
.editor
.markup
.TextAttributes
;
40 import com
.intellij
.openapi
.util
.Comparing
;
41 import com
.intellij
.openapi
.util
.Pair
;
42 import com
.intellij
.openapi
.util
.TextRange
;
43 import com
.intellij
.profile
.codeInspection
.InspectionProjectProfileManager
;
44 import com
.intellij
.psi
.PsiElement
;
45 import com
.intellij
.psi
.impl
.source
.SourceTreeToPsiMap
;
46 import com
.intellij
.util
.ArrayUtil
;
47 import com
.intellij
.xml
.util
.XmlStringUtil
;
48 import org
.jetbrains
.annotations
.NonNls
;
49 import org
.jetbrains
.annotations
.NotNull
;
50 import org
.jetbrains
.annotations
.Nullable
;
54 import java
.util
.Arrays
;
55 import java
.util
.List
;
57 public class HighlightInfo
{
58 public static final HighlightInfo
[] EMPTY_ARRAY
= new HighlightInfo
[0];
59 private static final Logger LOG
= Logger
.getInstance("#com.intellij.codeInsight.daemon.impl.HighlightInfo");
60 private final boolean myNeedsUpdateOnTyping
;
61 public JComponent fileLevelComponent
;
62 public final TextAttributes forcedTextAttributes
;
64 public HighlightSeverity
getSeverity() {
68 public TextAttributes
getTextAttributes(final PsiElement element
) {
69 return forcedTextAttributes
== null ?
getAttributesByType(element
, type
) : forcedTextAttributes
;
72 public static TextAttributes
getAttributesByType(@Nullable final PsiElement element
, @NotNull HighlightInfoType type
) {
73 final TextAttributes textAttributes
= SeverityRegistrar
.getInstance(element
!= null ? element
.getProject() : null).getTextAttributesBySeverity(type
.getSeverity(element
));
74 if (textAttributes
!= null) {
75 return textAttributes
;
77 EditorColorsScheme scheme
= EditorColorsManager
.getInstance().getGlobalScheme();
78 TextAttributesKey key
= type
.getAttributesKey();
79 return scheme
.getAttributes(key
);
83 public Color
getErrorStripeMarkColor(final PsiElement element
) {
84 if (forcedTextAttributes
!= null && forcedTextAttributes
.getErrorStripeColor() != null) {
85 return forcedTextAttributes
.getErrorStripeColor();
87 if (severity
== HighlightSeverity
.ERROR
) {
88 return EditorColorsManager
.getInstance().getGlobalScheme().getAttributes(CodeInsightColors
.ERRORS_ATTRIBUTES
).getErrorStripeColor();
90 if (severity
== HighlightSeverity
.WARNING
) {
91 return EditorColorsManager
.getInstance().getGlobalScheme().getAttributes(CodeInsightColors
.WARNINGS_ATTRIBUTES
).getErrorStripeColor();
93 if (severity
== HighlightSeverity
.INFO
){
94 return EditorColorsManager
.getInstance().getGlobalScheme().getAttributes(CodeInsightColors
.INFO_ATTRIBUTES
).getErrorStripeColor();
96 if (severity
== HighlightSeverity
.GENERIC_SERVER_ERROR_OR_WARNING
) {
97 return EditorColorsManager
.getInstance().getGlobalScheme().getAttributes(CodeInsightColors
.GENERIC_SERVER_ERROR_OR_WARNING
).getErrorStripeColor();
100 TextAttributes attributes
= getAttributesByType(element
, type
);
101 return attributes
== null ?
null : attributes
.getErrorStripeColor();
105 public static HighlightInfo
createHighlightInfo(@NotNull HighlightInfoType type
, @NotNull PsiElement element
, String description
) {
106 return createHighlightInfo(type
, element
, description
, htmlEscapeToolTip(description
));
111 public static String
htmlEscapeToolTip(String description
) {
112 return description
== null ?
null : "<html><body>"+ XmlStringUtil
.escapeString(description
)+"</body></html>";
115 public static HighlightInfo
createHighlightInfo(@NotNull HighlightInfoType type
, @NotNull PsiElement element
, String description
, String toolTip
) {
116 TextRange range
= element
.getTextRange();
117 int start
= range
.getStartOffset();
118 int end
= range
.getEndOffset();
119 return createHighlightInfo(type
, element
, start
, end
, description
, toolTip
);
122 public static HighlightInfo
createHighlightInfo(@NotNull HighlightInfoType type
, @Nullable PsiElement element
, int start
, int end
, String description
,
125 TextAttributes forcedAttributes
) {
126 LOG
.assertTrue(ArrayUtil
.find(HighlightSeverity
.DEFAULT_SEVERITIES
, type
.getSeverity(element
)) != -1 || element
!= null, "Custom type demands element to detect text attributes");
127 HighlightInfo highlightInfo
= new HighlightInfo(forcedAttributes
, type
, start
, end
, description
, toolTip
, type
.getSeverity(null), isEndOfLine
, null, false);
128 for (HighlightInfoFilter filter
: getFilters()) {
129 if (!filter
.accept(highlightInfo
, element
!= null ? element
.getContainingFile() : null)) {
133 return highlightInfo
;
135 public static HighlightInfo
createHighlightInfo(@NotNull HighlightInfoType type
, @Nullable PsiElement element
, int start
, int end
, String description
, String toolTip
) {
136 return createHighlightInfo(type
, element
, start
, end
, description
, toolTip
, false, null);
139 @NotNull private static HighlightInfoFilter
[] getFilters() {
140 return ApplicationManager
.getApplication().getExtensions(HighlightInfoFilter
.EXTENSION_POINT_NAME
);
143 public static HighlightInfo
createHighlightInfo(@NotNull HighlightInfoType type
, int start
, int end
, String description
) {
144 return createHighlightInfo(type
, null, start
, end
, description
, htmlEscapeToolTip(description
));
147 public static HighlightInfo
createHighlightInfo(@NotNull HighlightInfoType type
, @NotNull TextRange textRange
, String description
) {
148 return createHighlightInfo(type
, textRange
.getStartOffset(), textRange
.getEndOffset(), description
);
151 public static HighlightInfo
createHighlightInfo(@NotNull HighlightInfoType type
, @NotNull TextRange textRange
, String description
, String toolTip
, TextAttributes textAttributes
) {
152 // do not use HighlightInfoFilter
153 return new HighlightInfo(textAttributes
, type
, textRange
.getStartOffset(), textRange
.getEndOffset(), description
, htmlEscapeToolTip(toolTip
),type
.getSeverity(null), false, null,
157 public boolean needUpdateOnTyping() {
158 return myNeedsUpdateOnTyping
;
161 public final HighlightInfoType type
;
163 public final int startOffset
;
164 public final int endOffset
;
166 public int fixStartOffset
;
167 public int fixEndOffset
;
168 public RangeMarker fixMarker
;
170 public final String description
;
171 public final String toolTip
;
172 public final HighlightSeverity severity
;
174 public final boolean isAfterEndOfLine
;
175 public final boolean isFileLevelAnnotation
;
176 public int navigationShift
= 0;
178 public RangeHighlighterEx highlighter
;
181 public List
<Pair
<IntentionActionDescriptor
, TextRange
>> quickFixActionRanges
;
182 public List
<Pair
<IntentionActionDescriptor
, RangeMarker
>> quickFixActionMarkers
;
183 private boolean hasHint
;
185 private GutterIconRenderer gutterIconRenderer
;
187 public HighlightInfo(HighlightInfoType type
, int startOffset
, int endOffset
, String description
, String toolTip
) {
188 this(null, type
, startOffset
, endOffset
, description
, toolTip
, type
.getSeverity(null), false, null, false);
191 public HighlightInfo(@Nullable TextAttributes textAttributes
,
192 @NotNull HighlightInfoType type
,
195 @Nullable String description
,
196 @Nullable String toolTip
,
197 @NotNull HighlightSeverity severity
,
198 boolean afterEndOfLine
,
199 @Nullable Boolean needsUpdateOnTyping
, boolean isFileLevelAnnotation
) {
200 if (startOffset
< 0 || startOffset
> endOffset
) {
201 LOG
.error("Incorrect highlightInfo bounds. description="+description
+"; startOffset="+startOffset
+"; endOffset="+endOffset
+";type="+type
);
203 forcedTextAttributes
= textAttributes
;
205 this.startOffset
= startOffset
;
206 this.endOffset
= endOffset
;
207 fixStartOffset
= startOffset
;
208 fixEndOffset
= endOffset
;
209 this.description
= description
;
210 this.toolTip
= toolTip
;
211 this.severity
= severity
;
212 isAfterEndOfLine
= afterEndOfLine
;
213 myNeedsUpdateOnTyping
= calcNeedUpdateOnTyping(needsUpdateOnTyping
, type
);
214 this.isFileLevelAnnotation
= isFileLevelAnnotation
;
217 private static boolean calcNeedUpdateOnTyping(Boolean needsUpdateOnTyping
, HighlightInfoType type
) {
218 if (needsUpdateOnTyping
!= null) return needsUpdateOnTyping
.booleanValue();
220 if (type
== HighlightInfoType
.TODO
) return false;
221 if (type
== HighlightInfoType
.LOCAL_VARIABLE
) return false;
222 if (type
== HighlightInfoType
.INSTANCE_FIELD
) return false;
223 if (type
== HighlightInfoType
.STATIC_FIELD
) return false;
224 if (type
== HighlightInfoType
.PARAMETER
) return false;
225 if (type
== HighlightInfoType
.METHOD_CALL
) return false;
226 if (type
== HighlightInfoType
.METHOD_DECLARATION
) return false;
227 if (type
== HighlightInfoType
.STATIC_METHOD
) return false;
228 if (type
== HighlightInfoType
.CONSTRUCTOR_CALL
) return false;
229 if (type
== HighlightInfoType
.CONSTRUCTOR_DECLARATION
) return false;
230 if (type
== HighlightInfoType
.INTERFACE_NAME
) return false;
231 if (type
== HighlightInfoType
.ABSTRACT_CLASS_NAME
) return false;
232 if (type
== HighlightInfoType
.CLASS_NAME
) return false;
236 public boolean equals(Object obj
) {
237 if (obj
== this) return true;
238 if (!(obj
instanceof HighlightInfo
)) return false;
239 HighlightInfo info
= (HighlightInfo
)obj
;
241 return info
.getSeverity() == getSeverity() &&
242 info
.startOffset
== startOffset
&&
243 info
.endOffset
== endOffset
&&
244 Comparing
.equal(info
.type
, type
) &&
245 Comparing
.equal(info
.gutterIconRenderer
, gutterIconRenderer
) &&
246 Comparing
.equal(info
.forcedTextAttributes
, forcedTextAttributes
) &&
247 Comparing
.strEqual(info
.description
, description
);
250 public boolean equalsByActualOffset(HighlightInfo info
) {
251 if (info
== this) return true;
253 return info
.getSeverity() == getSeverity() &&
254 info
.getActualStartOffset() == getActualStartOffset() &&
255 info
.getActualEndOffset() == getActualEndOffset() &&
256 Comparing
.equal(info
.type
, type
) &&
257 Comparing
.equal(info
.gutterIconRenderer
, gutterIconRenderer
) &&
258 Comparing
.equal(info
.forcedTextAttributes
, forcedTextAttributes
) &&
259 Comparing
.strEqual(info
.description
, description
);
262 public int hashCode() {
267 public String
toString() {
268 @NonNls String s
= "HighlightInfo(" + startOffset
+ "," + endOffset
+")";
269 if (getActualStartOffset() != startOffset
|| getActualEndOffset() != endOffset
) {
270 s
+= "; actual: (" + getActualStartOffset() + "," + getActualEndOffset() + ")";
272 if (text
!= null) s
+= " text='" + text
+ "'";
273 if (description
!= null) s
+= ", description='" + description
+ "'";
274 if (toolTip
!= null) s
+= ", toolTip='" + toolTip
+ "'";
275 s
+= " severity=" + severity
;
279 public static HighlightInfo
createHighlightInfo(@NotNull HighlightInfoType type
, @NotNull ASTNode childByRole
, String localizedMessage
) {
280 return createHighlightInfo(type
, SourceTreeToPsiMap
.treeElementToPsi(childByRole
), localizedMessage
);
283 public GutterIconRenderer
getGutterIconRenderer() {
284 return gutterIconRenderer
;
287 public void setGutterIconRenderer(final GutterIconRenderer gutterIconRenderer
) {
288 this.gutterIconRenderer
= gutterIconRenderer
;
291 public static HighlightInfo
createHighlightInfo(@NotNull final HighlightInfoType type
,
292 @NotNull final PsiElement element
,
293 final String message
,
294 final TextAttributes attributes
) {
295 TextRange textRange
= element
.getTextRange();
296 // do not use HighlightInfoFilter
297 return new HighlightInfo(attributes
, type
, textRange
.getStartOffset(), textRange
.getEndOffset(), message
, htmlEscapeToolTip(message
),
298 type
.getSeverity(element
), false, Boolean
.FALSE
, false);
303 public static HighlightInfo
fromAnnotation(@NotNull Annotation annotation
) {
304 return fromAnnotation(annotation
, null);
307 public static HighlightInfo
fromAnnotation(@NotNull Annotation annotation
, @Nullable TextRange fixedRange
) {
308 TextAttributes attributes
= annotation
.getEnforcedTextAttributes();
309 if (attributes
== null) {
310 attributes
= EditorColorsManager
.getInstance().getGlobalScheme().getAttributes(annotation
.getTextAttributes());
312 HighlightInfo info
= new HighlightInfo(attributes
, convertType(annotation
),
313 fixedRange
!= null? fixedRange
.getStartOffset() : annotation
.getStartOffset(),
314 fixedRange
!= null? fixedRange
.getEndOffset() : annotation
.getEndOffset(),
315 annotation
.getMessage(), annotation
.getTooltip(),
316 annotation
.getSeverity(), annotation
.isAfterEndOfLine(), annotation
.needsUpdateOnTyping(), annotation
.isFileLevelAnnotation());
317 info
.setGutterIconRenderer(annotation
.getGutterIconRenderer());
318 List
<Annotation
.QuickFixInfo
> fixes
= annotation
.getQuickFixes();
320 for (final Annotation
.QuickFixInfo quickFixInfo
: fixes
) {
321 QuickFixAction
.registerQuickFixAction(info
, fixedRange
!= null? fixedRange
: quickFixInfo
.textRange
, quickFixInfo
.quickFix
, quickFixInfo
.key
);
327 public static HighlightInfoType
convertType(Annotation annotation
) {
328 ProblemHighlightType type
= annotation
.getHighlightType();
329 if (type
== ProblemHighlightType
.LIKE_UNUSED_SYMBOL
) return HighlightInfoType
.UNUSED_SYMBOL
;
330 if (type
== ProblemHighlightType
.LIKE_UNKNOWN_SYMBOL
) return HighlightInfoType
.WRONG_REF
;
331 if (type
== ProblemHighlightType
.LIKE_DEPRECATED
) return HighlightInfoType
.DEPRECATED
;
332 return convertSeverity(annotation
.getSeverity());
335 public static HighlightInfoType
convertSeverity(final HighlightSeverity severity
) {
336 return severity
== HighlightSeverity
.ERROR
337 ? HighlightInfoType
.ERROR
338 : severity
== HighlightSeverity
.WARNING ? HighlightInfoType
.WARNING
339 : severity
== HighlightSeverity
.INFO ? HighlightInfoType
.INFO
: HighlightInfoType
.INFORMATION
;
342 public static ProblemHighlightType
convertType(HighlightInfoType infoType
) {
343 if (infoType
== HighlightInfoType
.ERROR
|| infoType
== HighlightInfoType
.WRONG_REF
) return ProblemHighlightType
.ERROR
;
344 if (infoType
== HighlightInfoType
.WARNING
) return ProblemHighlightType
.GENERIC_ERROR_OR_WARNING
;
345 if (infoType
== HighlightInfoType
.INFORMATION
) return ProblemHighlightType
.INFORMATION
;
346 return ProblemHighlightType
.INFO
;
349 public static ProblemHighlightType
convertSeverityToProblemHighlight(HighlightSeverity severity
) {
350 return severity
== HighlightSeverity
.ERROR? ProblemHighlightType
.ERROR
:
351 severity
== HighlightSeverity
.WARNING ? ProblemHighlightType
.GENERIC_ERROR_OR_WARNING
:
352 severity
== HighlightSeverity
.INFO ? ProblemHighlightType
.INFO
: ProblemHighlightType
.INFORMATION
;
356 public boolean hasHint() {
360 public void setHint(final boolean hasHint
) {
361 this.hasHint
= hasHint
;
364 public int getActualStartOffset() {
365 return highlighter
== null ? startOffset
: highlighter
.getStartOffset();
367 public int getActualEndOffset() {
368 return highlighter
== null ? endOffset
: highlighter
.getEndOffset();
371 public static class IntentionActionDescriptor
{
372 private final IntentionAction myAction
;
373 private volatile List
<IntentionAction
> myOptions
;
374 private volatile HighlightDisplayKey myKey
;
375 private final String myDisplayName
;
376 private final Icon myIcon
;
378 public IntentionActionDescriptor(@NotNull IntentionAction action
, final HighlightDisplayKey key
) {
379 this(action
, null, HighlightDisplayKey
.getDisplayNameByKey(key
), null);
383 public IntentionActionDescriptor(@NotNull IntentionAction action
, final List
<IntentionAction
> options
, final String displayName
) {
384 this(action
, options
, displayName
, null);
387 public IntentionActionDescriptor(@NotNull IntentionAction action
, final List
<IntentionAction
> options
, final String displayName
, Icon icon
) {
390 myDisplayName
= displayName
;
395 public IntentionAction
getAction() {
400 public List
<IntentionAction
> getOptions(@NotNull PsiElement element
) {
401 List
<IntentionAction
> options
= myOptions
;
402 HighlightDisplayKey key
= myKey
;
403 if (options
!= null || key
== null) {
406 List
<IntentionAction
> newOptions
= IntentionManager
.getInstance().getStandardIntentionOptions(key
, element
);
407 InspectionProfile profile
= InspectionProjectProfileManager
.getInstance(element
.getProject()).getInspectionProfile();
408 InspectionProfileEntry tool
= profile
.getInspectionTool(key
.toString(), element
);
409 if (!(tool
instanceof LocalInspectionToolWrapper
)) {
410 HighlightDisplayKey idkey
= HighlightDisplayKey
.findById(key
.toString());
412 tool
= profile
.getInspectionTool(idkey
.toString(), element
);
415 if (tool
instanceof LocalInspectionToolWrapper
) {
416 final LocalInspectionTool localInspectionTool
= ((LocalInspectionToolWrapper
)tool
).getTool();
417 Class aClass
= myAction
.getClass();
418 if (myAction
instanceof QuickFixWrapper
) {
419 aClass
= ((QuickFixWrapper
)myAction
).getFix().getClass();
421 newOptions
.add(new CleanupInspectionIntention(localInspectionTool
, aClass
));
422 if (localInspectionTool
instanceof CustomSuppressableInspectionTool
) {
423 final IntentionAction
[] suppressActions
= ((CustomSuppressableInspectionTool
)localInspectionTool
).getSuppressActions(element
);
424 if (suppressActions
!= null) {
425 newOptions
.addAll(Arrays
.asList(suppressActions
));
430 synchronized (this) {
432 if (options
== null) {
433 myOptions
= options
= newOptions
;
440 public String
getDisplayName() {
441 return myDisplayName
;
445 public String
toString() {
446 return "descriptor: " + getAction().getText();
449 public Icon
getIcon() {