e7fbf45a3362401c73c14a094bbc3a3b15e3953f
[fedora-idea.git] / platform / lang-impl / src / com / intellij / codeInsight / daemon / impl / HighlightInfo.java
blobe7fbf45a3362401c73c14a094bbc3a3b15e3953f
1 /*
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;
52 import javax.swing.*;
53 import java.awt.*;
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() {
65 return severity;
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);
82 @Nullable
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));
109 @Nullable
110 @NonNls
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,
123 String toolTip,
124 boolean isEndOfLine,
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)) {
130 return 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,
154 false);
157 public boolean needUpdateOnTyping() {
158 return myNeedsUpdateOnTyping;
161 public final HighlightInfoType type;
162 public int group;
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;
179 public String text;
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,
193 int startOffset,
194 int endOffset,
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;
204 this.type = type;
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;
233 return true;
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() {
263 return startOffset;
266 @NonNls
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;
276 return s;
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();
319 if (fixes != null) {
320 for (final Annotation.QuickFixInfo quickFixInfo : fixes) {
321 QuickFixAction.registerQuickFixAction(info, fixedRange != null? fixedRange : quickFixInfo.textRange, quickFixInfo.quickFix, quickFixInfo.key);
324 return info;
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() {
357 return 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);
380 myKey = key;
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) {
388 myAction = action;
389 myOptions = options;
390 myDisplayName = displayName;
391 myIcon = icon;
394 @NotNull
395 public IntentionAction getAction() {
396 return myAction;
399 @Nullable
400 public List<IntentionAction> getOptions(@NotNull PsiElement element) {
401 List<IntentionAction> options = myOptions;
402 HighlightDisplayKey key = myKey;
403 if (options != null || key == null) {
404 return options;
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());
411 if (idkey != null) {
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) {
431 options = myOptions;
432 if (options == null) {
433 myOptions = options = newOptions;
435 myKey = null;
437 return options;
440 public String getDisplayName() {
441 return myDisplayName;
444 @NonNls
445 public String toString() {
446 return "descriptor: " + getAction().getText();
449 public Icon getIcon() {
450 return myIcon;