CPP-778 Support for semantic per-variable highlighting (fix Darcula background, before code inspection pass, EP approach)

This commit is contained in:
Alexey Utkin
2016-06-29 15:33:28 +03:00
parent f4515a9c7d
commit afce3735a4
7 changed files with 123 additions and 79 deletions
@@ -15,27 +15,41 @@
*/
package com.intellij.codeHighlighting;
import com.intellij.codeInsight.daemon.RainbowProvider;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
import com.intellij.lang.Language;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.editor.DefaultLanguageHighlighterColors;
import com.intellij.openapi.editor.colors.TextAttributesScheme;
import com.intellij.openapi.editor.HighlighterColors;
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.markup.TextAttributes;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringHash;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.util.containers.hash.HashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
public class RainbowHighlighter {
private final float[] myFloats;
private static final HashSet<Language> BY_PASS_LANGUAGES = new HashSet<Language>();
@NotNull private final EditorColorsScheme myColorsScheme;
@NotNull private final Color myDefaultBackground;
public RainbowHighlighter(@NotNull TextAttributesScheme colorsScheme) {
float[] components = colorsScheme.getAttributes(DefaultLanguageHighlighterColors.CONSTANT).getForegroundColor().getRGBColorComponents(null);
private static final HashSet<Language> BY_PASS_LANGUAGES = new HashSet<Language>();
static {
RainbowProvider.initRainbow();
}
public RainbowHighlighter(@Nullable EditorColorsScheme colorsScheme, @Nullable Color background) {
myColorsScheme = colorsScheme != null ? colorsScheme : EditorColorsManager.getInstance().getGlobalScheme();
myDefaultBackground = background != null ? background : myColorsScheme.getDefaultBackground();
float[] components = myColorsScheme.getAttributes(DefaultLanguageHighlighterColors.CONSTANT).getForegroundColor().getRGBColorComponents(null);
myFloats = Color.RGBtoHSB((int)(255 * components[0]), (int)(255 * components[0]), (int)(255 * components[0]), null);
}
@@ -62,11 +76,34 @@ public class RainbowHighlighter {
//System.out.println("name = " + name + " \tv=" + v);
final Color color = Color.getHSBColor(v, 0.7f, myFloats[2] + .3f);
Color bkColor = origin.getBackgroundColor();
if (bkColor == null) {
bkColor = myColorsScheme.getAttributes(HighlighterColors.TEXT).getBackgroundColor();
}
if (bkColor == null) {
bkColor = myDefaultBackground;
}
return TextAttributes.fromFlyweight(origin
.getFlyweight()
.withForeground(color)
//fixme: uta: foreground color is not activated for local variables without background color reset
.withBackground(UIManager.getColor("EditorPane.background"))
.withBackground(bkColor)
);
}
public HighlightInfo getInfo(
@Nullable String nameKey,
@Nullable PsiElement id,
@Nullable TextAttributesKey colorKey) {
if (id == null || nameKey == null || StringUtil.isEmpty(nameKey)) return null;
if (colorKey == null) colorKey = DefaultLanguageHighlighterColors.LOCAL_VARIABLE;
final TextAttributes attributes = getAttributes(nameKey, myColorsScheme.getAttributes(colorKey));
return HighlightInfo
.newHighlightInfo(RAINBOW_ELEMENT)
.textAttributes(attributes)
.range(id)
.create();
}
}
@@ -0,0 +1,49 @@
/*
* Copyright 2000-2016 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;
import com.intellij.codeHighlighting.RainbowHighlighter;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public abstract class RainbowProvider {
static ExtensionPointName<RainbowProvider> EP_NAME = ExtensionPointName.create("com.intellij.rainbowProvider");
@NotNull
public static RainbowProvider[] getRainbowFileProcessors() {
return Extensions.getExtensions(EP_NAME);
}
public static void initRainbow() {
for (RainbowProvider processor : getRainbowFileProcessors()) {
processor.init();
}
}
public abstract void init();
public abstract boolean isValidContext(@NotNull final PsiFile file);
public abstract List<HighlightInfo> getHighlights(@NotNull PsiFile file,
@NotNull RainbowHighlighter highlighter,
@NotNull ProgressIndicator progress);
}
@@ -185,7 +185,7 @@ public class HighlightInfo implements Segment {
isLikeVariable(type.getAttributesKey())) {
String text = element.getContainingFile().getText();
String name = text.substring(startOffset, endOffset);
attributes = new RainbowHighlighter(colorsScheme).getAttributes(name, attributes);
attributes = new RainbowHighlighter(colorsScheme, null).getAttributes(name, attributes);
}
return attributes;
}
@@ -18,75 +18,46 @@ package com.intellij.codeInsight.daemon.impl;
import com.intellij.codeHighlighting.RainbowHighlighter;
import com.intellij.codeHighlighting.TextEditorHighlightingPass;
import com.intellij.openapi.editor.DefaultLanguageHighlighterColors;
import com.intellij.codeInsight.daemon.RainbowProvider;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.editor.impl.EditorImpl;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
public class RainbowIdentifierHighlighterPass extends TextEditorHighlightingPass {
protected final PsiFile myFile;
protected final RainbowHighlighter myRainbowHighlighter;
protected List<HighlightInfo> toHighlight;
protected final EditorColorsScheme myEditorColorsScheme;
protected List<HighlightInfo> myHighlight;
protected final Color myBackgroundColor;
protected RainbowIdentifierHighlighterPass(@NotNull PsiFile file, @NotNull Editor editor) {
super(file.getProject(), editor.getDocument(), false);
myBackgroundColor = (editor instanceof EditorImpl) ? ((EditorImpl)editor).getBackgroundColor() : null;
myFile = file;
myEditorColorsScheme = editor.getColorsScheme();
myRainbowHighlighter = new RainbowHighlighter(myEditorColorsScheme);
}
@Override
public void doCollectInformation(@NotNull final ProgressIndicator progress) {
// reference implementation!
final List<HighlightInfo> infos = new ArrayList<>();
myFile.accept(new PsiRecursiveElementWalkingVisitor() {
@Override
public void visitElement(PsiElement e) {
final HighlightInfo attrs;
if (e instanceof PsiReference) {
attrs = getInfo(e.getText(), e, null);
}
else if (e instanceof PsiNameIdentifierOwner) {
PsiNameIdentifierOwner identifierOwner = (PsiNameIdentifierOwner)e;
attrs = getInfo(identifierOwner.getName(), identifierOwner.getNameIdentifier(), null);
}
else {
attrs = null;
}
if (attrs != null) {
infos.add(attrs);
}
super.visitElement(e);
// myBackgroundColor takes into account "read only" editors
final RainbowHighlighter rainbowHighlighter = new RainbowHighlighter(getColorsScheme(), myBackgroundColor);
for (RainbowProvider processor : RainbowProvider.getRainbowFileProcessors()) {
if (processor.isValidContext(myFile)) {
infos.addAll(processor.getHighlights(myFile, rainbowHighlighter, progress));
}
});
toHighlight = infos;
}
myHighlight = infos;
}
@Override
public void doApplyInformationToEditor() {
if (toHighlight == null || myDocument == null) return;
UpdateHighlightersUtil.setHighlightersToEditor(myProject, myDocument, 0, myFile.getTextLength(), toHighlight, getColorsScheme(), getId());
}
protected HighlightInfo getInfo(@Nullable String nameKey, @Nullable PsiElement id, @Nullable TextAttributesKey colorKey) {
if (id == null || nameKey == null || StringUtil.isEmpty(nameKey)) return null;
if (colorKey == null) colorKey = DefaultLanguageHighlighterColors.LOCAL_VARIABLE;
final TextAttributes attributes = myRainbowHighlighter.getAttributes(nameKey,
myEditorColorsScheme.getAttributes(colorKey));
return HighlightInfo
.newHighlightInfo(RainbowHighlighter.RAINBOW_ELEMENT)
.textAttributes(attributes)
.range(id)
.create();
if (myHighlight == null || myDocument == null) return;
UpdateHighlightersUtil
.setHighlightersToEditor(myProject, myDocument, 0, myFile.getTextLength(), myHighlight, getColorsScheme(), getId());
}
}
@@ -23,13 +23,13 @@ import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
/**
* Language-dependent implementation need to be inherited from this class and
*/
public class RainbowIdentifierHighlighterPassFactory extends AbstractProjectComponent implements TextEditorHighlightingPassFactory {
public RainbowIdentifierHighlighterPassFactory(Project project, TextEditorHighlightingPassRegistrar highlightingPassRegistrar) {
super(project);
highlightingPassRegistrar.registerTextEditorHighlightingPass(this, new int[]{Pass.UPDATE_ALL}, null, false, -1);
highlightingPassRegistrar.registerTextEditorHighlightingPass(this,
TextEditorHighlightingPassRegistrar.Anchor.BEFORE,
Pass.UPDATE_FOLDING,
false, false);
}
@Override
@@ -41,29 +41,9 @@ public class RainbowIdentifierHighlighterPassFactory extends AbstractProjectComp
@Override
public TextEditorHighlightingPass createHighlightingPass(@NotNull final PsiFile file, @NotNull final Editor editor) {
if (RainbowHighlighter.isRainbowEnabled() && isValidContext(file, editor)) {
return getRainbowPass(file, editor);
if (RainbowHighlighter.isRainbowEnabled()) {
return new RainbowIdentifierHighlighterPass(file, editor);
}
return null;
}
/**
* Need to be rewritten in language-dependent implementation.
* Default implementation colors all identifiers in [PsiReference] and [PsiNameIdentifierOwner] elements.
*/
@NotNull
protected RainbowIdentifierHighlighterPass getRainbowPass(@NotNull PsiFile file, @NotNull Editor editor) {
return new RainbowIdentifierHighlighterPass(file, editor);
}
/**
* Need to be rewritten in language-dependent implementation.
* For example:
* return file instanceof JavaFile;
* Default implementation works for any language.
*/
protected boolean isValidContext(@NotNull final PsiFile file, @NotNull Editor editor) {
return true;
}
}
@@ -95,6 +95,9 @@
<extensionPoint name="severitiesProvider"
interface="com.intellij.codeInsight.daemon.impl.SeveritiesProvider"/>
<extensionPoint name="rainbowProvider"
interface="com.intellij.codeInsight.daemon.RainbowProvider"/>
<extensionPoint name="implicitUsageProvider"
interface="com.intellij.codeInsight.daemon.ImplicitUsageProvider"/>
@@ -154,6 +154,10 @@
<implementation-class>com.intellij.codeInsight.daemon.impl.CodeFoldingPassFactory</implementation-class>
<skipForDefaultProject/>
</component>
<component>
<implementation-class>com.intellij.codeInsight.daemon.impl.RainbowIdentifierHighlighterPassFactory</implementation-class>
<skipForDefaultProject/>
</component>
<component>
<implementation-class>com.intellij.codeInsight.daemon.impl.IndentsPassFactory</implementation-class>
<skipForDefaultProject/>