2 * Copyright 2000-2016 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.
16 package com.intellij.codeHighlighting;
18 import com.intellij.codeInsight.daemon.impl.HighlightInfo;
19 import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
20 import com.intellij.lang.annotation.HighlightSeverity;
21 import com.intellij.openapi.application.ApplicationManager;
22 import com.intellij.openapi.editor.DefaultLanguageHighlighterColors;
23 import com.intellij.openapi.editor.colors.EditorColorsManager;
24 import com.intellij.openapi.editor.colors.TextAttributesKey;
25 import com.intellij.openapi.editor.colors.TextAttributesScheme;
26 import com.intellij.openapi.editor.markup.TextAttributes;
27 import com.intellij.openapi.util.registry.Registry;
28 import com.intellij.openapi.util.text.StringHash;
29 import com.intellij.openapi.util.text.StringUtil;
30 import com.intellij.psi.PsiElement;
31 import com.intellij.ui.ColorUtil;
32 import com.intellij.ui.JBColor;
33 import com.intellij.util.containers.ContainerUtil;
34 import org.jetbrains.annotations.Contract;
35 import org.jetbrains.annotations.NotNull;
36 import org.jetbrains.annotations.Nullable;
39 import java.util.List;
41 public class RainbowHighlighter {
42 private static final JBColor[] RAINBOW_JB_COLORS_DEFAULT = {
43 new JBColor(0x9b3b6a, 0x529d52),
44 new JBColor(0x114d77, 0xbe7070),
45 new JBColor(0xbc8650, 0x3d7676),
46 new JBColor(0x005910, 0xbe9970),
47 new JBColor(0xbc5150, 0x9d527c),
49 public static final TextAttributesKey[] RAINBOW_COLOR_KEYS = new TextAttributesKey[RAINBOW_JB_COLORS_DEFAULT.length];
50 private static final int RAINBOW_COLORS_BETWEEN = 4;
51 private static final String UNIT_TEST_COLORS = "#000001,#000002,#000003,#000004"; // Do not modify!
53 for (int i = 0; i < RAINBOW_JB_COLORS_DEFAULT.length; ++i) {
54 JBColor jbColor = RAINBOW_JB_COLORS_DEFAULT[i];
55 TextAttributes textAttributes = new TextAttributes(jbColor, null, null, null, Font.PLAIN);
56 RAINBOW_COLOR_KEYS[i] = TextAttributesKey.createTextAttributesKey("RAINBOW_COLOR" + i, textAttributes);
59 public final static String RAINBOW_TYPE = "rainbow";
60 private final static String RAINBOW_TEMP_PREF = "RAINBOW_TEMP_";
62 @NotNull private final TextAttributesScheme myColorsScheme;
63 @NotNull private final Color[] myRainbowColors;
65 public RainbowHighlighter(@Nullable TextAttributesScheme colorsScheme) {
66 myColorsScheme = colorsScheme != null ? colorsScheme : EditorColorsManager.getInstance().getGlobalScheme();
67 myRainbowColors = generateColorSequence(myColorsScheme);
70 public static final HighlightInfoType RAINBOW_ELEMENT = new HighlightInfoType.HighlightInfoTypeImpl(HighlightSeverity.INFORMATION, DefaultLanguageHighlighterColors.CONSTANT);
72 public static boolean isRainbowEnabled() {
73 return Registry.is("editor.rainbow.identifiers", false);
76 public static void setRainbowEnabled(boolean enabled) {
77 Registry.get("editor.rainbow.identifiers").setValue(enabled);
80 public static int hashColor(@NotNull String name, int colorsCount) {
81 return Math.abs(StringHash.murmur(name, 0x55AA)) % colorsCount;
84 public static int getColorIndex(@NotNull int[] index2usage, int hashedIndex, int colorsCount) {
85 int minIndex1 = indexOfMin(index2usage, hashedIndex, colorsCount);
86 int minIndex2 = indexOfMin(index2usage, 0, hashedIndex);
87 return index2usage[minIndex1] <= index2usage[minIndex2] ? minIndex1 : minIndex2;
90 @Contract(pure = true)
91 private static int indexOfMin(@NotNull int[] index2usage, int start, int end) {
92 int min = Integer.MAX_VALUE;
94 for (int i = start; i < end; i++) {
95 int value = index2usage[i];
105 @Contract(pure = true)
106 private Color calculateForeground(int colorIndex) {
107 return myRainbowColors[colorIndex];
110 public int getColorsCount() {
111 return myRainbowColors.length;
115 private static Color[] generateColorSequence(@NotNull TextAttributesScheme colorsScheme) {
116 String colorDump = ApplicationManager.getApplication().isUnitTestMode()
118 : Registry.get("rainbow.highlighter.colors").asString();
120 final List<String> registryColors = StringUtil.split(colorDump, ",");
121 if (!registryColors.isEmpty()) {
122 return registryColors.stream().map(s -> ColorUtil.fromHex(s.trim())).toArray(Color[]::new);
125 List<Color> stopColors = ContainerUtil.map(RAINBOW_COLOR_KEYS, key -> colorsScheme.getAttributes(key).getForegroundColor());
126 List<Color> colors = ColorGenerator.generateLinearColorSequence(stopColors, RAINBOW_COLORS_BETWEEN);
127 return colors.toArray(new Color[colors.size()]);
131 public TextAttributesKey[] getRainbowTempKeys() {
132 TextAttributesKey[] keys = new TextAttributesKey[myRainbowColors.length];
133 for (int i = 0; i < myRainbowColors.length; ++i) {
134 TextAttributesKey key = TextAttributesKey.createTextAttributesKey(RAINBOW_TEMP_PREF + i, new TextAttributes());
135 key.getDefaultAttributes().setForegroundColor(myRainbowColors[i]);
141 public static boolean isRainbowTempKey(TextAttributesKey key) {
142 return key.getExternalName().startsWith(RAINBOW_TEMP_PREF);
145 public HighlightInfo getInfo(int colorIndex, @Nullable PsiElement id, @Nullable TextAttributesKey colorKey) {
146 return id == null ? null : getInfoBuilder(colorIndex, colorKey).range(id).create();
149 public HighlightInfo getInfo(int colorIndex, int start, int end, @Nullable TextAttributesKey colorKey) {
150 return getInfoBuilder(colorIndex, colorKey).range(start, end).create();
154 protected HighlightInfo.Builder getInfoBuilder(int colorIndex, @Nullable TextAttributesKey colorKey) {
155 if (colorKey == null) {
156 colorKey = DefaultLanguageHighlighterColors.LOCAL_VARIABLE;
159 .newHighlightInfo(RAINBOW_ELEMENT)
160 .textAttributes(TextAttributes
161 .fromFlyweight(myColorsScheme
162 .getAttributes(colorKey)
164 .withForeground(calculateForeground(colorIndex))));