diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/HighlightInfo.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/HighlightInfo.java index a92e40caeb3d..289c06c46bbf 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/HighlightInfo.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/HighlightInfo.java @@ -1,589 +1,590 @@ -/* - * Copyright 2000-2009 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.intellij.codeInsight.daemon.impl; - -import com.intellij.codeInsight.daemon.HighlightDisplayKey; -import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction; -import com.intellij.codeInsight.intention.IntentionAction; -import com.intellij.codeInsight.intention.IntentionManager; -import com.intellij.codeInspection.*; -import com.intellij.codeInspection.actions.CleanupInspectionIntention; -import com.intellij.codeInspection.ex.GlobalInspectionToolWrapper; -import com.intellij.codeInspection.ex.LocalInspectionToolWrapper; -import com.intellij.codeInspection.ex.QuickFixWrapper; -import com.intellij.lang.ASTNode; -import com.intellij.lang.annotation.Annotation; -import com.intellij.lang.annotation.HighlightSeverity; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.RangeMarker; -import com.intellij.openapi.editor.colors.CodeInsightColors; -import com.intellij.openapi.editor.colors.EditorColorsManager; -import com.intellij.openapi.editor.colors.EditorColorsScheme; -import com.intellij.openapi.editor.colors.TextAttributesKey; -import com.intellij.openapi.editor.ex.RangeHighlighterEx; -import com.intellij.openapi.editor.markup.GutterIconRenderer; -import com.intellij.openapi.editor.markup.TextAttributes; -import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.Segment; -import com.intellij.openapi.util.TextRange; -import com.intellij.profile.codeInspection.InspectionProjectProfileManager; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.util.ArrayUtil; -import com.intellij.util.containers.ContainerUtil; -import com.intellij.xml.util.XmlStringUtil; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import java.awt.*; -import java.util.List; - -public class HighlightInfo implements Segment { - private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.HighlightInfo"); - - public static final HighlightInfo[] EMPTY_ARRAY = new HighlightInfo[0]; - private final boolean myNeedsUpdateOnTyping; - public JComponent fileLevelComponent; - public final TextAttributes forcedTextAttributes; - public final TextAttributesKey forcedTextAttributesKey; - - public final HighlightInfoType type; - public int group; - public final int startOffset; - public final int endOffset; - - public int fixStartOffset; - public int fixEndOffset; - public RangeMarker fixMarker; // null means it the same as highlighter - - public final String description; - public final String toolTip; - public final HighlightSeverity severity; - - public final boolean isAfterEndOfLine; - public final boolean isFileLevelAnnotation; - public int navigationShift = 0; - - public RangeHighlighterEx highlighter; - public String text; - - public List> quickFixActionRanges; - public List> quickFixActionMarkers; - private boolean hasHint; - public boolean fromInjection; - - private GutterIconRenderer gutterIconRenderer; - private String myProblemGroup; - public volatile boolean bijective; - - public HighlightSeverity getSeverity() { - return severity; - } - - @Nullable - public TextAttributes getTextAttributes(@Nullable final PsiElement element, @Nullable final EditorColorsScheme editorColorsScheme) { - if (forcedTextAttributes != null) { - return forcedTextAttributes; - } - - final EditorColorsScheme colorsScheme = getColorsScheme(editorColorsScheme); - if (colorsScheme == null) { - return null; - } - - if (forcedTextAttributesKey != null) { - return colorsScheme.getAttributes(forcedTextAttributesKey); - } - - return getAttributesByType(element, type, colorsScheme); - } - - public static TextAttributes getAttributesByType(@Nullable final PsiElement element, - @NotNull HighlightInfoType type, - @NotNull EditorColorsScheme colorsScheme) { - final SeverityRegistrar severityRegistrar = SeverityRegistrar.getInstance(element != null ? element.getProject() : null); - final TextAttributes textAttributes = severityRegistrar.getTextAttributesBySeverity(type.getSeverity(element)); - if (textAttributes != null) { - return textAttributes; - } - TextAttributesKey key = type.getAttributesKey(); - return colorsScheme.getAttributes(key); - } - - @Nullable - public Color getErrorStripeMarkColor(@NotNull PsiElement element, - @Nullable final EditorColorsScheme colorsScheme) { // if null global scheme will be used - if (forcedTextAttributes != null && forcedTextAttributes.getErrorStripeColor() != null) { - return forcedTextAttributes.getErrorStripeColor(); - } - final EditorColorsScheme scheme = getColorsScheme(colorsScheme); - if (scheme == null) { - return null; - } - if (forcedTextAttributesKey != null) { - final Color errorStripeColor = scheme.getAttributes(forcedTextAttributesKey).getErrorStripeColor(); - // let's copy above behaviour of forcedTextAttributes stripe color, but I'm not sure that the behaviour is correct in general - if (errorStripeColor != null) { - return errorStripeColor; - } - } - - if (severity == HighlightSeverity.ERROR) { - return scheme.getAttributes(CodeInsightColors.ERRORS_ATTRIBUTES).getErrorStripeColor(); - } - if (severity == HighlightSeverity.WARNING) { - return scheme.getAttributes(CodeInsightColors.WARNINGS_ATTRIBUTES).getErrorStripeColor(); - } - if (severity == HighlightSeverity.INFO){ - return scheme.getAttributes(CodeInsightColors.INFO_ATTRIBUTES).getErrorStripeColor(); - } - if (severity == HighlightSeverity.WEAK_WARNING){ - return scheme.getAttributes(CodeInsightColors.WEAK_WARNING_ATTRIBUTES).getErrorStripeColor(); - } - if (severity == HighlightSeverity.GENERIC_SERVER_ERROR_OR_WARNING) { - return scheme.getAttributes(CodeInsightColors.GENERIC_SERVER_ERROR_OR_WARNING).getErrorStripeColor(); - } - - TextAttributes attributes = getAttributesByType(element, type, scheme); - return attributes == null ? null : attributes.getErrorStripeColor(); - - } - - @Nullable - private static EditorColorsScheme getColorsScheme(@Nullable final EditorColorsScheme customScheme) { - if (customScheme != null) { - return customScheme; - } - return EditorColorsManager.getInstance().getGlobalScheme(); - } - - @Nullable - public static HighlightInfo createHighlightInfo(@NotNull HighlightInfoType type, - @NotNull PsiElement element, - @Nullable String description) - { - return createHighlightInfo(type, element, description, htmlEscapeToolTip(description)); - } - - @Nullable - @NonNls - public static String htmlEscapeToolTip(@Nullable String description) { - return description == null ? null : ""+ XmlStringUtil.escapeString(description)+""; - } - - @Nullable - public static HighlightInfo createHighlightInfo(@NotNull HighlightInfoType type, @NotNull PsiElement element, String description, String toolTip) { - TextRange range = element.getTextRange(); - int start = range.getStartOffset(); - int end = range.getEndOffset(); - return createHighlightInfo(type, element, start, end, description, toolTip); - } - - @Nullable - public static HighlightInfo createHighlightInfo(@NotNull HighlightInfoType type, @Nullable PsiElement element, int start, int end, String description, - String toolTip, - boolean isEndOfLine, - @Nullable TextAttributes forcedAttributes) { - LOG.assertTrue(element != null || ArrayUtil.find(HighlightSeverity.DEFAULT_SEVERITIES, type.getSeverity(element)) != -1, "Custom type demands element to detect its text attributes"); - HighlightInfo highlightInfo = new HighlightInfo(forcedAttributes, null, type, start, end, description, toolTip, - type.getSeverity(element), isEndOfLine, null, false); - PsiFile file = element == null ? null : element.getContainingFile(); - for (HighlightInfoFilter filter : getFilters()) { - if (!filter.accept(highlightInfo, file)) { - return null; - } - } - return highlightInfo; - } - @Nullable - public static HighlightInfo createHighlightInfo(@NotNull HighlightInfoType type, @Nullable PsiElement element, int start, int end, String description, String toolTip) { - return createHighlightInfo(type, element, start, end, description, toolTip, false, null); - } - - @NotNull - private static HighlightInfoFilter[] getFilters() { - return ApplicationManager.getApplication().getExtensions(HighlightInfoFilter.EXTENSION_POINT_NAME); - } - - @Nullable - public static HighlightInfo createHighlightInfo(@NotNull HighlightInfoType type, int start, int end, String description) { - return createHighlightInfo(type, null, start, end, description, htmlEscapeToolTip(description)); - } - - @Nullable - public static HighlightInfo createHighlightInfo(@NotNull HighlightInfoType type, @NotNull TextRange textRange, String description) { - return createHighlightInfo(type, textRange.getStartOffset(), textRange.getEndOffset(), description); - } - - public static HighlightInfo createHighlightInfo(@NotNull HighlightInfoType type, @NotNull TextRange textRange, String description, String toolTip, TextAttributes textAttributes) { - // do not use HighlightInfoFilter - return new HighlightInfo(textAttributes, null, type, textRange.getStartOffset(), textRange.getEndOffset(), description, - htmlEscapeToolTip(toolTip), type.getSeverity(null), false, null, false); - } - - public boolean needUpdateOnTyping() { - return myNeedsUpdateOnTyping; - } - - public HighlightInfo(@NotNull HighlightInfoType type, int startOffset, int endOffset, String description, String toolTip) { - this(null, null, type, startOffset, endOffset, description, toolTip, type.getSeverity(null), false, null, false); - } - - //primary - public HighlightInfo(@Nullable TextAttributes forcedTextAttributes, - @Nullable TextAttributesKey forcedTextAttributesKey, - @NotNull HighlightInfoType type, - int startOffset, - int endOffset, - @Nullable String description, - @Nullable String toolTip, - @NotNull HighlightSeverity severity, - boolean afterEndOfLine, - @Nullable Boolean needsUpdateOnTyping, - boolean isFileLevelAnnotation) { - if (startOffset < 0 || startOffset > endOffset) { - LOG.error("Incorrect highlightInfo bounds. description="+description+"; startOffset="+startOffset+"; endOffset="+endOffset+";type="+type); - } - this.forcedTextAttributes = forcedTextAttributes; - this.forcedTextAttributesKey = forcedTextAttributesKey; - this.type = type; - this.startOffset = startOffset; - this.endOffset = endOffset; - fixStartOffset = startOffset; - fixEndOffset = endOffset; - this.description = description; - this.toolTip = toolTip; - this.severity = severity; - isAfterEndOfLine = afterEndOfLine; - myNeedsUpdateOnTyping = calcNeedUpdateOnTyping(needsUpdateOnTyping, type); - this.isFileLevelAnnotation = isFileLevelAnnotation; - } - - private static boolean calcNeedUpdateOnTyping(Boolean needsUpdateOnTyping, HighlightInfoType type) { - if (needsUpdateOnTyping != null) return needsUpdateOnTyping.booleanValue(); - - if (type == HighlightInfoType.TODO) return false; - if (type == HighlightInfoType.LOCAL_VARIABLE) return false; - if (type == HighlightInfoType.INSTANCE_FIELD) return false; - if (type == HighlightInfoType.STATIC_FIELD) return false; - if (type == HighlightInfoType.PARAMETER) return false; - if (type == HighlightInfoType.METHOD_CALL) return false; - if (type == HighlightInfoType.METHOD_DECLARATION) return false; - if (type == HighlightInfoType.STATIC_METHOD) return false; - if (type == HighlightInfoType.CONSTRUCTOR_CALL) return false; - if (type == HighlightInfoType.CONSTRUCTOR_DECLARATION) return false; - if (type == HighlightInfoType.INTERFACE_NAME) return false; - if (type == HighlightInfoType.ABSTRACT_CLASS_NAME) return false; - if (type == HighlightInfoType.CLASS_NAME) return false; - if (type == HighlightInfoType.ANONYMOUS_CLASS_NAME) return false; - return true; - } - - public boolean equals(Object obj) { - if (obj == this) return true; - if (!(obj instanceof HighlightInfo)) return false; - HighlightInfo info = (HighlightInfo)obj; - - return info.getSeverity() == getSeverity() && - info.startOffset == startOffset && - info.endOffset == endOffset && - Comparing.equal(info.type, type) && - Comparing.equal(info.gutterIconRenderer, gutterIconRenderer) && - Comparing.equal(info.forcedTextAttributes, forcedTextAttributes) && - Comparing.equal(info.forcedTextAttributesKey, forcedTextAttributesKey) && - Comparing.strEqual(info.description, description); - } - - public boolean equalsByActualOffset(HighlightInfo info) { - if (info == this) return true; - - return info.getSeverity() == getSeverity() && - info.getActualStartOffset() == getActualStartOffset() && - info.getActualEndOffset() == getActualEndOffset() && - Comparing.equal(info.type, type) && - Comparing.equal(info.gutterIconRenderer, gutterIconRenderer) && - Comparing.equal(info.forcedTextAttributes, forcedTextAttributes) && - Comparing.equal(info.forcedTextAttributesKey, forcedTextAttributesKey) && - Comparing.strEqual(info.description, description); - } - - public int hashCode() { - return startOffset; - } - - @NonNls - public String toString() { - @NonNls String s = "HighlightInfo(" + startOffset + "," + endOffset+")"; - if (getActualStartOffset() != startOffset || getActualEndOffset() != endOffset) { - s += "; actual: (" + getActualStartOffset() + "," + getActualEndOffset() + ")"; - } - if (text != null) s += " text='" + text + "'"; - if (description != null) s+= ", description='" + description + "'"; - s += " severity=" + severity; - s += " group=" + group; - - if (quickFixActionRanges != null) { - s+= "; quickFixes: "+quickFixActionRanges; - } - if (gutterIconRenderer != null) { - s += "; gutter: " + gutterIconRenderer; - } - return s; - } - - @Nullable - public static HighlightInfo createHighlightInfo(@NotNull HighlightInfoType type, @NotNull ASTNode childByRole, String localizedMessage) { - return createHighlightInfo(type, childByRole.getPsi(), localizedMessage); - } - - public GutterIconRenderer getGutterIconRenderer() { - return gutterIconRenderer; - } - - public void setGutterIconRenderer(final GutterIconRenderer gutterIconRenderer) { - this.gutterIconRenderer = gutterIconRenderer; - } - - @Nullable - public String getProblemGroup() { - return myProblemGroup; - } - - public void setProblemGroup(@Nullable String problemGroup) { - myProblemGroup = problemGroup; - } - - public static HighlightInfo createHighlightInfo(@NotNull final HighlightInfoType type, - @NotNull final PsiElement element, - @Nullable final String message, - @Nullable final TextAttributes attributes) { - TextRange textRange = element.getTextRange(); - // do not use HighlightInfoFilter - return new HighlightInfo(attributes, null, type, textRange.getStartOffset(), textRange.getEndOffset(), message, - htmlEscapeToolTip(message), type.getSeverity(element), false, Boolean.FALSE, false); - } - - public static HighlightInfo createHighlightInfo(@NotNull final HighlightInfoType type, - @NotNull final PsiElement element, - @Nullable final String message, - @Nullable final TextAttributesKey attributesKey) { - TextRange textRange = element.getTextRange(); - // do not use HighlightInfoFilter - return new HighlightInfo(null, attributesKey, type, textRange.getStartOffset(), textRange.getEndOffset(), message, - htmlEscapeToolTip(message), type.getSeverity(element), false, Boolean.FALSE, false); - } - - - public static HighlightInfo fromAnnotation(@NotNull Annotation annotation) { - return fromAnnotation(annotation, null); - } - - public static HighlightInfo fromAnnotation(@NotNull Annotation annotation, @Nullable TextRange fixedRange) { - final TextAttributes forcedAttributes = annotation.getEnforcedTextAttributes(); - final TextAttributesKey forcedAttributesKey = forcedAttributes == null ? annotation.getTextAttributes() : null; - - HighlightInfo info = new HighlightInfo(forcedAttributes, forcedAttributesKey, convertType(annotation), - fixedRange != null? fixedRange.getStartOffset() : annotation.getStartOffset(), - fixedRange != null? fixedRange.getEndOffset() : annotation.getEndOffset(), - annotation.getMessage(), annotation.getTooltip(), - annotation.getSeverity(), annotation.isAfterEndOfLine(), annotation.needsUpdateOnTyping(), annotation.isFileLevelAnnotation()); - info.setGutterIconRenderer(annotation.getGutterIconRenderer()); - info.setProblemGroup(annotation.getProblemGroup()); - List fixes = annotation.getQuickFixes(); - if (fixes != null) { - for (final Annotation.QuickFixInfo quickFixInfo : fixes) { - QuickFixAction.registerQuickFixAction(info, fixedRange != null ? fixedRange : quickFixInfo.textRange, quickFixInfo.quickFix, - quickFixInfo.key != null ? quickFixInfo.key : HighlightDisplayKey.find(DefaultHighlightVisitorBasedInspection.AnnotatorBasedInspection.ANNOTATOR_SHORT_NAME)); - } - } - return info; - } - - public static HighlightInfoType convertType(Annotation annotation) { - ProblemHighlightType type = annotation.getHighlightType(); - if (type == ProblemHighlightType.LIKE_UNUSED_SYMBOL) return HighlightInfoType.UNUSED_SYMBOL; - if (type == ProblemHighlightType.LIKE_UNKNOWN_SYMBOL) return HighlightInfoType.WRONG_REF; - if (type == ProblemHighlightType.LIKE_DEPRECATED) return HighlightInfoType.DEPRECATED; - return convertSeverity(annotation.getSeverity()); - } - - public static HighlightInfoType convertSeverity(final HighlightSeverity severity) { - return severity == HighlightSeverity.ERROR - ? HighlightInfoType.ERROR - : severity == HighlightSeverity.WARNING ? HighlightInfoType.WARNING - : severity == HighlightSeverity.INFO ? HighlightInfoType.INFO - : severity == HighlightSeverity.WEAK_WARNING ? HighlightInfoType.WEAK_WARNING : HighlightInfoType.INFORMATION; - } - - public static ProblemHighlightType convertType(HighlightInfoType infoType) { - if (infoType == HighlightInfoType.ERROR || infoType == HighlightInfoType.WRONG_REF) return ProblemHighlightType.ERROR; - if (infoType == HighlightInfoType.WARNING) return ProblemHighlightType.GENERIC_ERROR_OR_WARNING; - if (infoType == HighlightInfoType.INFORMATION) return ProblemHighlightType.INFORMATION; - return ProblemHighlightType.WEAK_WARNING; - } - - public static ProblemHighlightType convertSeverityToProblemHighlight(HighlightSeverity severity) { - return severity == HighlightSeverity.ERROR? ProblemHighlightType.ERROR : - severity == HighlightSeverity.WARNING ? ProblemHighlightType.GENERIC_ERROR_OR_WARNING : - severity == HighlightSeverity.INFO ? ProblemHighlightType.INFO : - severity == HighlightSeverity.WEAK_WARNING? ProblemHighlightType.WEAK_WARNING : ProblemHighlightType.INFORMATION; - } - - - public boolean hasHint() { - return hasHint; - } - - public void setHint(final boolean hasHint) { - this.hasHint = hasHint; - } - - public int getActualStartOffset() { - RangeHighlighterEx h = highlighter; - return h == null || !h.isValid() ? startOffset : h.getStartOffset(); - } - public int getActualEndOffset() { - RangeHighlighterEx h = highlighter; - return h == null || !h.isValid() ? endOffset : h.getEndOffset(); - } - - //public void setCustomColorScheme(@Nullable final EditorColorsScheme customColorScheme) { - // myCustomColorScheme = customColorScheme; - //} - - public static class IntentionActionDescriptor { - private final IntentionAction myAction; - private volatile List myOptions; - private volatile HighlightDisplayKey myKey; - private final String myDisplayName; - private final Icon myIcon; - - public IntentionActionDescriptor(@NotNull IntentionAction action, final List options, final String displayName) { - this(action, options, displayName, null); - } - - public IntentionActionDescriptor(@NotNull IntentionAction action, final Icon icon) { - this(action, null, null, icon); - } - - public IntentionActionDescriptor(@NotNull IntentionAction action, @Nullable final List options, @Nullable final String displayName, @Nullable Icon icon) { - this(action, options, displayName, icon, null); - } - - public IntentionActionDescriptor(@NotNull IntentionAction action, final List options, final String displayName, Icon icon, @Nullable HighlightDisplayKey key) { - myAction = action; - myOptions = options; - myDisplayName = displayName; - myIcon = icon; - myKey = key; - } - - @NotNull - public IntentionAction getAction() { - return myAction; - } - - @Nullable - public List getOptions(@NotNull PsiElement element, @Nullable Editor editor) { - if (editor != null && Boolean.FALSE.equals(editor.getUserData(IntentionManager.SHOW_INTENTION_OPTIONS_KEY))) { - return null; - } - List options = myOptions; - HighlightDisplayKey key = myKey; - if (options != null || key == null) { - return options; - } - List newOptions = IntentionManager.getInstance().getStandardIntentionOptions(key, element); - InspectionProfile profile = InspectionProjectProfileManager.getInstance(element.getProject()).getInspectionProfile(); - InspectionProfileEntry tool = profile.getInspectionTool(key.toString(), element); - if (!(tool instanceof LocalInspectionToolWrapper)) { - HighlightDisplayKey idkey = HighlightDisplayKey.findById(key.toString()); - if (idkey != null) { - tool = profile.getInspectionTool(idkey.toString(), element); - } - } - InspectionProfileEntry wrappedTool = tool; - if (tool instanceof LocalInspectionToolWrapper) { - wrappedTool = ((LocalInspectionToolWrapper)tool).getTool(); - Class aClass = myAction.getClass(); - if (myAction instanceof QuickFixWrapper) { - aClass = ((QuickFixWrapper)myAction).getFix().getClass(); - } - newOptions.add(new CleanupInspectionIntention((LocalInspectionToolWrapper)tool, aClass)); - } else if (tool instanceof GlobalInspectionToolWrapper) { - wrappedTool = ((GlobalInspectionToolWrapper)tool).getTool(); - if (wrappedTool instanceof GlobalSimpleInspectionTool && (myAction instanceof LocalQuickFix || myAction instanceof QuickFixWrapper)) { - Class aClass = myAction.getClass(); - if (myAction instanceof QuickFixWrapper) { - aClass = ((QuickFixWrapper)myAction).getFix().getClass(); - } - newOptions.add(new CleanupInspectionIntention((GlobalInspectionToolWrapper)tool, aClass)); - } - } - - if (wrappedTool instanceof CustomSuppressableInspectionTool) { - final IntentionAction[] suppressActions = ((CustomSuppressableInspectionTool)wrappedTool).getSuppressActions(element); - if (suppressActions != null) { - ContainerUtil.addAll(newOptions, suppressActions); - } - } - - synchronized (this) { - options = myOptions; - if (options == null) { - myOptions = options = newOptions; - } - myKey = null; - } - return options; - } - - public String getDisplayName() { - return myDisplayName; - } - - @NonNls - public String toString() { - String text = getAction().getText(); - return "descriptor: " + (text.isEmpty() ? getAction().getClass() : text); - } - - public Icon getIcon() { - return myIcon; - } - - @Override - public boolean equals(Object obj) { - return obj instanceof IntentionActionDescriptor && myAction.equals(((IntentionActionDescriptor)obj).myAction); - } - } - - @Override - public int getStartOffset() { - return getActualStartOffset(); - } - - @Override - public int getEndOffset() { - return getActualEndOffset(); - } -} +/* + * Copyright 2000-2009 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.intellij.codeInsight.daemon.impl; + +import com.intellij.codeInsight.daemon.HighlightDisplayKey; +import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction; +import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInsight.intention.IntentionManager; +import com.intellij.codeInspection.*; +import com.intellij.codeInspection.actions.CleanupInspectionIntention; +import com.intellij.codeInspection.ex.GlobalInspectionToolWrapper; +import com.intellij.codeInspection.ex.LocalInspectionToolWrapper; +import com.intellij.codeInspection.ex.QuickFixWrapper; +import com.intellij.lang.ASTNode; +import com.intellij.lang.annotation.Annotation; +import com.intellij.lang.annotation.HighlightSeverity; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.RangeMarker; +import com.intellij.openapi.editor.colors.CodeInsightColors; +import com.intellij.openapi.editor.colors.EditorColorsManager; +import com.intellij.openapi.editor.colors.EditorColorsScheme; +import com.intellij.openapi.editor.colors.TextAttributesKey; +import com.intellij.openapi.editor.ex.RangeHighlighterEx; +import com.intellij.openapi.editor.markup.GutterIconRenderer; +import com.intellij.openapi.editor.markup.TextAttributes; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.Segment; +import com.intellij.openapi.util.TextRange; +import com.intellij.profile.codeInspection.InspectionProjectProfileManager; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.util.ArrayUtil; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.xml.util.XmlStringUtil; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.util.List; + +public class HighlightInfo implements Segment { + private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.HighlightInfo"); + + public static final HighlightInfo[] EMPTY_ARRAY = new HighlightInfo[0]; + private final boolean myNeedsUpdateOnTyping; + public JComponent fileLevelComponent; + public final TextAttributes forcedTextAttributes; + public final TextAttributesKey forcedTextAttributesKey; + + public final HighlightInfoType type; + public int group; + public final int startOffset; + public final int endOffset; + + public int fixStartOffset; + public int fixEndOffset; + public RangeMarker fixMarker; // null means it the same as highlighter + + public final String description; + public final String toolTip; + public final HighlightSeverity severity; + + public final boolean isAfterEndOfLine; + public final boolean isFileLevelAnnotation; + public int navigationShift = 0; + + public RangeHighlighterEx highlighter; + public String text; + + public List> quickFixActionRanges; + public List> quickFixActionMarkers; + private boolean hasHint; + public boolean fromInjection; + + private GutterIconRenderer gutterIconRenderer; + private String myProblemGroup; + public volatile boolean bijective; + + public HighlightSeverity getSeverity() { + return severity; + } + + @Nullable + public TextAttributes getTextAttributes(@Nullable final PsiElement element, @Nullable final EditorColorsScheme editorColorsScheme) { + if (forcedTextAttributes != null) { + return forcedTextAttributes; + } + + final EditorColorsScheme colorsScheme = getColorsScheme(editorColorsScheme); + if (colorsScheme == null) { + return null; + } + + if (forcedTextAttributesKey != null) { + return colorsScheme.getAttributes(forcedTextAttributesKey); + } + + return getAttributesByType(element, type, colorsScheme); + } + + public static TextAttributes getAttributesByType(@Nullable final PsiElement element, + @NotNull HighlightInfoType type, + @NotNull EditorColorsScheme colorsScheme) { + final SeverityRegistrar severityRegistrar = SeverityRegistrar.getInstance(element != null ? element.getProject() : null); + final TextAttributes textAttributes = severityRegistrar.getTextAttributesBySeverity(type.getSeverity(element)); + if (textAttributes != null) { + return textAttributes; + } + TextAttributesKey key = type.getAttributesKey(); + return colorsScheme.getAttributes(key); + } + + @Nullable + public Color getErrorStripeMarkColor(@NotNull PsiElement element, + @Nullable final EditorColorsScheme colorsScheme) { // if null global scheme will be used + if (forcedTextAttributes != null && forcedTextAttributes.getErrorStripeColor() != null) { + return forcedTextAttributes.getErrorStripeColor(); + } + final EditorColorsScheme scheme = getColorsScheme(colorsScheme); + if (scheme == null) { + return null; + } + if (forcedTextAttributesKey != null) { + final Color errorStripeColor = scheme.getAttributes(forcedTextAttributesKey).getErrorStripeColor(); + // let's copy above behaviour of forcedTextAttributes stripe color, but I'm not sure that the behaviour is correct in general + if (errorStripeColor != null) { + return errorStripeColor; + } + } + + if (severity == HighlightSeverity.ERROR) { + return scheme.getAttributes(CodeInsightColors.ERRORS_ATTRIBUTES).getErrorStripeColor(); + } + if (severity == HighlightSeverity.WARNING) { + return scheme.getAttributes(CodeInsightColors.WARNINGS_ATTRIBUTES).getErrorStripeColor(); + } + if (severity == HighlightSeverity.INFO){ + return scheme.getAttributes(CodeInsightColors.INFO_ATTRIBUTES).getErrorStripeColor(); + } + if (severity == HighlightSeverity.WEAK_WARNING){ + return scheme.getAttributes(CodeInsightColors.WEAK_WARNING_ATTRIBUTES).getErrorStripeColor(); + } + if (severity == HighlightSeverity.GENERIC_SERVER_ERROR_OR_WARNING) { + return scheme.getAttributes(CodeInsightColors.GENERIC_SERVER_ERROR_OR_WARNING).getErrorStripeColor(); + } + + TextAttributes attributes = getAttributesByType(element, type, scheme); + return attributes == null ? null : attributes.getErrorStripeColor(); + + } + + @Nullable + private static EditorColorsScheme getColorsScheme(@Nullable final EditorColorsScheme customScheme) { + if (customScheme != null) { + return customScheme; + } + return EditorColorsManager.getInstance().getGlobalScheme(); + } + + @Nullable + public static HighlightInfo createHighlightInfo(@NotNull HighlightInfoType type, + @NotNull PsiElement element, + @Nullable String description) + { + return createHighlightInfo(type, element, description, htmlEscapeToolTip(description)); + } + + @Nullable + @NonNls + public static String htmlEscapeToolTip(@Nullable String description) { + return description == null ? null : ""+ XmlStringUtil.escapeString(description)+""; + } + + @Nullable + public static HighlightInfo createHighlightInfo(@NotNull HighlightInfoType type, @NotNull PsiElement element, String description, String toolTip) { + TextRange range = element.getTextRange(); + int start = range.getStartOffset(); + int end = range.getEndOffset(); + return createHighlightInfo(type, element, start, end, description, toolTip); + } + + @Nullable + public static HighlightInfo createHighlightInfo(@NotNull HighlightInfoType type, @Nullable PsiElement element, int start, int end, String description, + String toolTip, + boolean isEndOfLine, + @Nullable TextAttributes forcedAttributes) { + LOG.assertTrue(element != null || ArrayUtil.find(HighlightSeverity.DEFAULT_SEVERITIES, type.getSeverity(element)) != -1, "Custom type demands element to detect its text attributes"); + HighlightInfo highlightInfo = new HighlightInfo(forcedAttributes, null, type, start, end, description, toolTip, + type.getSeverity(element), isEndOfLine, null, false); + PsiFile file = element == null ? null : element.getContainingFile(); + for (HighlightInfoFilter filter : getFilters()) { + if (!filter.accept(highlightInfo, file)) { + return null; + } + } + return highlightInfo; + } + @Nullable + public static HighlightInfo createHighlightInfo(@NotNull HighlightInfoType type, @Nullable PsiElement element, int start, int end, String description, String toolTip) { + return createHighlightInfo(type, element, start, end, description, toolTip, false, null); + } + + @NotNull + private static HighlightInfoFilter[] getFilters() { + return ApplicationManager.getApplication().getExtensions(HighlightInfoFilter.EXTENSION_POINT_NAME); + } + + @Nullable + public static HighlightInfo createHighlightInfo(@NotNull HighlightInfoType type, int start, int end, @Nullable String description) { + return createHighlightInfo(type, null, start, end, description, htmlEscapeToolTip(description)); + } + + @Nullable + public static HighlightInfo createHighlightInfo(@NotNull HighlightInfoType type, @NotNull TextRange textRange, @Nullable String description) { + return createHighlightInfo(type, textRange.getStartOffset(), textRange.getEndOffset(), description); + } + + public static HighlightInfo createHighlightInfo(@NotNull HighlightInfoType type, @NotNull TextRange textRange, + @Nullable String description, @Nullable String toolTip, @Nullable TextAttributes textAttributes) { + // do not use HighlightInfoFilter + return new HighlightInfo(textAttributes, null, type, textRange.getStartOffset(), textRange.getEndOffset(), description, + htmlEscapeToolTip(toolTip), type.getSeverity(null), false, null, false); + } + + public boolean needUpdateOnTyping() { + return myNeedsUpdateOnTyping; + } + + public HighlightInfo(@NotNull HighlightInfoType type, int startOffset, int endOffset, String description, String toolTip) { + this(null, null, type, startOffset, endOffset, description, toolTip, type.getSeverity(null), false, null, false); + } + + //primary + public HighlightInfo(@Nullable TextAttributes forcedTextAttributes, + @Nullable TextAttributesKey forcedTextAttributesKey, + @NotNull HighlightInfoType type, + int startOffset, + int endOffset, + @Nullable String description, + @Nullable String toolTip, + @NotNull HighlightSeverity severity, + boolean afterEndOfLine, + @Nullable Boolean needsUpdateOnTyping, + boolean isFileLevelAnnotation) { + if (startOffset < 0 || startOffset > endOffset) { + LOG.error("Incorrect highlightInfo bounds. description="+description+"; startOffset="+startOffset+"; endOffset="+endOffset+";type="+type); + } + this.forcedTextAttributes = forcedTextAttributes; + this.forcedTextAttributesKey = forcedTextAttributesKey; + this.type = type; + this.startOffset = startOffset; + this.endOffset = endOffset; + fixStartOffset = startOffset; + fixEndOffset = endOffset; + this.description = description; + this.toolTip = toolTip; + this.severity = severity; + isAfterEndOfLine = afterEndOfLine; + myNeedsUpdateOnTyping = calcNeedUpdateOnTyping(needsUpdateOnTyping, type); + this.isFileLevelAnnotation = isFileLevelAnnotation; + } + + private static boolean calcNeedUpdateOnTyping(Boolean needsUpdateOnTyping, HighlightInfoType type) { + if (needsUpdateOnTyping != null) return needsUpdateOnTyping.booleanValue(); + + if (type == HighlightInfoType.TODO) return false; + if (type == HighlightInfoType.LOCAL_VARIABLE) return false; + if (type == HighlightInfoType.INSTANCE_FIELD) return false; + if (type == HighlightInfoType.STATIC_FIELD) return false; + if (type == HighlightInfoType.PARAMETER) return false; + if (type == HighlightInfoType.METHOD_CALL) return false; + if (type == HighlightInfoType.METHOD_DECLARATION) return false; + if (type == HighlightInfoType.STATIC_METHOD) return false; + if (type == HighlightInfoType.CONSTRUCTOR_CALL) return false; + if (type == HighlightInfoType.CONSTRUCTOR_DECLARATION) return false; + if (type == HighlightInfoType.INTERFACE_NAME) return false; + if (type == HighlightInfoType.ABSTRACT_CLASS_NAME) return false; + if (type == HighlightInfoType.CLASS_NAME) return false; + if (type == HighlightInfoType.ANONYMOUS_CLASS_NAME) return false; + return true; + } + + public boolean equals(Object obj) { + if (obj == this) return true; + if (!(obj instanceof HighlightInfo)) return false; + HighlightInfo info = (HighlightInfo)obj; + + return info.getSeverity() == getSeverity() && + info.startOffset == startOffset && + info.endOffset == endOffset && + Comparing.equal(info.type, type) && + Comparing.equal(info.gutterIconRenderer, gutterIconRenderer) && + Comparing.equal(info.forcedTextAttributes, forcedTextAttributes) && + Comparing.equal(info.forcedTextAttributesKey, forcedTextAttributesKey) && + Comparing.strEqual(info.description, description); + } + + public boolean equalsByActualOffset(HighlightInfo info) { + if (info == this) return true; + + return info.getSeverity() == getSeverity() && + info.getActualStartOffset() == getActualStartOffset() && + info.getActualEndOffset() == getActualEndOffset() && + Comparing.equal(info.type, type) && + Comparing.equal(info.gutterIconRenderer, gutterIconRenderer) && + Comparing.equal(info.forcedTextAttributes, forcedTextAttributes) && + Comparing.equal(info.forcedTextAttributesKey, forcedTextAttributesKey) && + Comparing.strEqual(info.description, description); + } + + public int hashCode() { + return startOffset; + } + + @NonNls + public String toString() { + @NonNls String s = "HighlightInfo(" + startOffset + "," + endOffset+")"; + if (getActualStartOffset() != startOffset || getActualEndOffset() != endOffset) { + s += "; actual: (" + getActualStartOffset() + "," + getActualEndOffset() + ")"; + } + if (text != null) s += " text='" + text + "'"; + if (description != null) s+= ", description='" + description + "'"; + s += " severity=" + severity; + s += " group=" + group; + + if (quickFixActionRanges != null) { + s+= "; quickFixes: "+quickFixActionRanges; + } + if (gutterIconRenderer != null) { + s += "; gutter: " + gutterIconRenderer; + } + return s; + } + + @Nullable + public static HighlightInfo createHighlightInfo(@NotNull HighlightInfoType type, @NotNull ASTNode childByRole, String localizedMessage) { + return createHighlightInfo(type, childByRole.getPsi(), localizedMessage); + } + + public GutterIconRenderer getGutterIconRenderer() { + return gutterIconRenderer; + } + + public void setGutterIconRenderer(final GutterIconRenderer gutterIconRenderer) { + this.gutterIconRenderer = gutterIconRenderer; + } + + @Nullable + public String getProblemGroup() { + return myProblemGroup; + } + + public void setProblemGroup(@Nullable String problemGroup) { + myProblemGroup = problemGroup; + } + + public static HighlightInfo createHighlightInfo(@NotNull final HighlightInfoType type, + @NotNull final PsiElement element, + @Nullable final String message, + @Nullable final TextAttributes attributes) { + TextRange textRange = element.getTextRange(); + // do not use HighlightInfoFilter + return new HighlightInfo(attributes, null, type, textRange.getStartOffset(), textRange.getEndOffset(), message, + htmlEscapeToolTip(message), type.getSeverity(element), false, Boolean.FALSE, false); + } + + public static HighlightInfo createHighlightInfo(@NotNull final HighlightInfoType type, + @NotNull final PsiElement element, + @Nullable final String message, + @Nullable final TextAttributesKey attributesKey) { + TextRange textRange = element.getTextRange(); + // do not use HighlightInfoFilter + return new HighlightInfo(null, attributesKey, type, textRange.getStartOffset(), textRange.getEndOffset(), message, + htmlEscapeToolTip(message), type.getSeverity(element), false, Boolean.FALSE, false); + } + + + public static HighlightInfo fromAnnotation(@NotNull Annotation annotation) { + return fromAnnotation(annotation, null); + } + + public static HighlightInfo fromAnnotation(@NotNull Annotation annotation, @Nullable TextRange fixedRange) { + final TextAttributes forcedAttributes = annotation.getEnforcedTextAttributes(); + final TextAttributesKey forcedAttributesKey = forcedAttributes == null ? annotation.getTextAttributes() : null; + + HighlightInfo info = new HighlightInfo(forcedAttributes, forcedAttributesKey, convertType(annotation), + fixedRange != null? fixedRange.getStartOffset() : annotation.getStartOffset(), + fixedRange != null? fixedRange.getEndOffset() : annotation.getEndOffset(), + annotation.getMessage(), annotation.getTooltip(), + annotation.getSeverity(), annotation.isAfterEndOfLine(), annotation.needsUpdateOnTyping(), annotation.isFileLevelAnnotation()); + info.setGutterIconRenderer(annotation.getGutterIconRenderer()); + info.setProblemGroup(annotation.getProblemGroup()); + List fixes = annotation.getQuickFixes(); + if (fixes != null) { + for (final Annotation.QuickFixInfo quickFixInfo : fixes) { + QuickFixAction.registerQuickFixAction(info, fixedRange != null ? fixedRange : quickFixInfo.textRange, quickFixInfo.quickFix, + quickFixInfo.key != null ? quickFixInfo.key : HighlightDisplayKey.find(DefaultHighlightVisitorBasedInspection.AnnotatorBasedInspection.ANNOTATOR_SHORT_NAME)); + } + } + return info; + } + + public static HighlightInfoType convertType(Annotation annotation) { + ProblemHighlightType type = annotation.getHighlightType(); + if (type == ProblemHighlightType.LIKE_UNUSED_SYMBOL) return HighlightInfoType.UNUSED_SYMBOL; + if (type == ProblemHighlightType.LIKE_UNKNOWN_SYMBOL) return HighlightInfoType.WRONG_REF; + if (type == ProblemHighlightType.LIKE_DEPRECATED) return HighlightInfoType.DEPRECATED; + return convertSeverity(annotation.getSeverity()); + } + + public static HighlightInfoType convertSeverity(final HighlightSeverity severity) { + return severity == HighlightSeverity.ERROR + ? HighlightInfoType.ERROR + : severity == HighlightSeverity.WARNING ? HighlightInfoType.WARNING + : severity == HighlightSeverity.INFO ? HighlightInfoType.INFO + : severity == HighlightSeverity.WEAK_WARNING ? HighlightInfoType.WEAK_WARNING : HighlightInfoType.INFORMATION; + } + + public static ProblemHighlightType convertType(HighlightInfoType infoType) { + if (infoType == HighlightInfoType.ERROR || infoType == HighlightInfoType.WRONG_REF) return ProblemHighlightType.ERROR; + if (infoType == HighlightInfoType.WARNING) return ProblemHighlightType.GENERIC_ERROR_OR_WARNING; + if (infoType == HighlightInfoType.INFORMATION) return ProblemHighlightType.INFORMATION; + return ProblemHighlightType.WEAK_WARNING; + } + + public static ProblemHighlightType convertSeverityToProblemHighlight(HighlightSeverity severity) { + return severity == HighlightSeverity.ERROR? ProblemHighlightType.ERROR : + severity == HighlightSeverity.WARNING ? ProblemHighlightType.GENERIC_ERROR_OR_WARNING : + severity == HighlightSeverity.INFO ? ProblemHighlightType.INFO : + severity == HighlightSeverity.WEAK_WARNING? ProblemHighlightType.WEAK_WARNING : ProblemHighlightType.INFORMATION; + } + + + public boolean hasHint() { + return hasHint; + } + + public void setHint(final boolean hasHint) { + this.hasHint = hasHint; + } + + public int getActualStartOffset() { + RangeHighlighterEx h = highlighter; + return h == null || !h.isValid() ? startOffset : h.getStartOffset(); + } + public int getActualEndOffset() { + RangeHighlighterEx h = highlighter; + return h == null || !h.isValid() ? endOffset : h.getEndOffset(); + } + + //public void setCustomColorScheme(@Nullable final EditorColorsScheme customColorScheme) { + // myCustomColorScheme = customColorScheme; + //} + + public static class IntentionActionDescriptor { + private final IntentionAction myAction; + private volatile List myOptions; + private volatile HighlightDisplayKey myKey; + private final String myDisplayName; + private final Icon myIcon; + + public IntentionActionDescriptor(@NotNull IntentionAction action, final List options, final String displayName) { + this(action, options, displayName, null); + } + + public IntentionActionDescriptor(@NotNull IntentionAction action, final Icon icon) { + this(action, null, null, icon); + } + + public IntentionActionDescriptor(@NotNull IntentionAction action, @Nullable final List options, @Nullable final String displayName, @Nullable Icon icon) { + this(action, options, displayName, icon, null); + } + + public IntentionActionDescriptor(@NotNull IntentionAction action, final List options, final String displayName, Icon icon, @Nullable HighlightDisplayKey key) { + myAction = action; + myOptions = options; + myDisplayName = displayName; + myIcon = icon; + myKey = key; + } + + @NotNull + public IntentionAction getAction() { + return myAction; + } + + @Nullable + public List getOptions(@NotNull PsiElement element, @Nullable Editor editor) { + if (editor != null && Boolean.FALSE.equals(editor.getUserData(IntentionManager.SHOW_INTENTION_OPTIONS_KEY))) { + return null; + } + List options = myOptions; + HighlightDisplayKey key = myKey; + if (options != null || key == null) { + return options; + } + List newOptions = IntentionManager.getInstance().getStandardIntentionOptions(key, element); + InspectionProfile profile = InspectionProjectProfileManager.getInstance(element.getProject()).getInspectionProfile(); + InspectionProfileEntry tool = profile.getInspectionTool(key.toString(), element); + if (!(tool instanceof LocalInspectionToolWrapper)) { + HighlightDisplayKey idkey = HighlightDisplayKey.findById(key.toString()); + if (idkey != null) { + tool = profile.getInspectionTool(idkey.toString(), element); + } + } + InspectionProfileEntry wrappedTool = tool; + if (tool instanceof LocalInspectionToolWrapper) { + wrappedTool = ((LocalInspectionToolWrapper)tool).getTool(); + Class aClass = myAction.getClass(); + if (myAction instanceof QuickFixWrapper) { + aClass = ((QuickFixWrapper)myAction).getFix().getClass(); + } + newOptions.add(new CleanupInspectionIntention((LocalInspectionToolWrapper)tool, aClass)); + } else if (tool instanceof GlobalInspectionToolWrapper) { + wrappedTool = ((GlobalInspectionToolWrapper)tool).getTool(); + if (wrappedTool instanceof GlobalSimpleInspectionTool && (myAction instanceof LocalQuickFix || myAction instanceof QuickFixWrapper)) { + Class aClass = myAction.getClass(); + if (myAction instanceof QuickFixWrapper) { + aClass = ((QuickFixWrapper)myAction).getFix().getClass(); + } + newOptions.add(new CleanupInspectionIntention((GlobalInspectionToolWrapper)tool, aClass)); + } + } + + if (wrappedTool instanceof CustomSuppressableInspectionTool) { + final IntentionAction[] suppressActions = ((CustomSuppressableInspectionTool)wrappedTool).getSuppressActions(element); + if (suppressActions != null) { + ContainerUtil.addAll(newOptions, suppressActions); + } + } + + synchronized (this) { + options = myOptions; + if (options == null) { + myOptions = options = newOptions; + } + myKey = null; + } + return options; + } + + public String getDisplayName() { + return myDisplayName; + } + + @NonNls + public String toString() { + String text = getAction().getText(); + return "descriptor: " + (text.isEmpty() ? getAction().getClass() : text); + } + + public Icon getIcon() { + return myIcon; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof IntentionActionDescriptor && myAction.equals(((IntentionActionDescriptor)obj).myAction); + } + } + + @Override + public int getStartOffset() { + return getActualStartOffset(); + } + + @Override + public int getEndOffset() { + return getActualEndOffset(); + } +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/IdentifierHighlighterPass.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/IdentifierHighlighterPass.java index 2012c5c95773..c2db6f0b7c31 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/IdentifierHighlighterPass.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/IdentifierHighlighterPass.java @@ -19,6 +19,7 @@ package com.intellij.codeInsight.daemon.impl; import com.intellij.codeHighlighting.TextEditorHighlightingPass; import com.intellij.codeInsight.CodeInsightSettings; import com.intellij.codeInsight.TargetElementUtilBase; +import com.intellij.codeInsight.highlighting.HighlightHandlerBase; import com.intellij.codeInsight.highlighting.HighlightUsagesHandler; import com.intellij.codeInsight.highlighting.HighlightUsagesHandlerBase; import com.intellij.codeInsight.highlighting.ReadWriteAccessDetector; @@ -32,6 +33,7 @@ import com.intellij.openapi.editor.markup.MarkupModel; import com.intellij.openapi.editor.markup.RangeHighlighter; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; @@ -44,10 +46,7 @@ import com.intellij.util.Processor; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; +import java.util.*; /** * @author yole @@ -147,16 +146,26 @@ public class IdentifierHighlighterPass extends TextEditorHighlightingPass { if (myReadAccessRanges.isEmpty() && myWriteAccessRanges.isEmpty()) { return Collections.emptyList(); } + Set> existingMarkupTooltips = new HashSet>(); + for (RangeHighlighter highlighter : myEditor.getMarkupModel().getAllHighlighters()) { + existingMarkupTooltips.add(Pair.create(highlighter.getErrorStripeTooltip(), new TextRange(highlighter.getStartOffset(), highlighter.getEndOffset()))); + } + List result = new ArrayList(myReadAccessRanges.size() + myWriteAccessRanges.size()); for (TextRange range: myReadAccessRanges) { - ContainerUtil.addIfNotNull(HighlightInfo.createHighlightInfo(ourReadHighlightInfoType, range, null),result); + ContainerUtil.addIfNotNull(createHighlightInfo(range, ourReadHighlightInfoType, existingMarkupTooltips), result); } for (TextRange range: myWriteAccessRanges) { - ContainerUtil.addIfNotNull(HighlightInfo.createHighlightInfo(ourWriteHighlightInfoType, range, null),result); + ContainerUtil.addIfNotNull(createHighlightInfo(range, ourWriteHighlightInfoType, existingMarkupTooltips), result); } return result; } + private HighlightInfo createHighlightInfo(TextRange range, HighlightInfoType type, Set> existingMarkupTooltips) { + String tooltip = HighlightHandlerBase.getLineTextErrorStripeTooltip(myDocument, range.getStartOffset()); + return HighlightInfo.createHighlightInfo(type, range, null, existingMarkupTooltips.contains(new Pair(tooltip, range)) ? null : tooltip, null); + } + public static void clearMyHighlights(Document document, Project project) { MarkupModel markupModel = DocumentMarkupModel.forDocument(document, project, true); for (RangeHighlighter highlighter : markupModel.getAllHighlighters()) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/highlighting/HighlightHandlerBase.java b/platform/lang-impl/src/com/intellij/codeInsight/highlighting/HighlightHandlerBase.java index 9854e703ae79..4e5e4adfe3e8 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/highlighting/HighlightHandlerBase.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/highlighting/HighlightHandlerBase.java @@ -19,7 +19,6 @@ package com.intellij.codeInsight.highlighting; import com.intellij.find.FindManager; import com.intellij.find.FindModel; import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.markup.RangeHighlighter; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; @@ -38,10 +37,9 @@ public abstract class HighlightHandlerBase { findManager.setFindNextModel(model); } - protected static void setLineTextErrorStripeTooltip(final RangeHighlighter highlighter) { - Document document = highlighter.getDocument(); - final int lineNumber = document.getLineNumber(highlighter.getStartOffset()); + public static String getLineTextErrorStripeTooltip(Document document, int offset) { + final int lineNumber = document.getLineNumber(offset); final String lineText = document.getText().substring(document.getLineStartOffset(lineNumber), document.getLineEndOffset(lineNumber)); - highlighter.setErrorStripeTooltip(" " + StringUtil.escapeXml(lineText.trim()) + " "); + return " " + StringUtil.escapeXml(lineText.trim()) + " "; } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/highlighting/HighlightUsagesHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/highlighting/HighlightUsagesHandler.java index f91feb6c5613..251826959233 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/highlighting/HighlightUsagesHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/highlighting/HighlightUsagesHandler.java @@ -314,7 +314,7 @@ public class HighlightUsagesHandler extends HighlightHandlerBase { highlightManager.addRangeHighlight(editor, range.getStartOffset(), range.getEndOffset(), attributes, false, highlighters); } for (RangeHighlighter highlighter : highlighters) { - setLineTextErrorStripeTooltip(highlighter); + highlighter.setErrorStripeTooltip(getLineTextErrorStripeTooltip(editor.getDocument(), highlighter.getStartOffset())); } }