IDEA-158861 resolve merge conflicts
[idea/community.git] / platform / analysis-impl / src / com / intellij / codeHighlighting / RainbowHighlighter.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.codeHighlighting;
17
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;
37
38 import java.awt.*;
39 import java.util.List;
40
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),
48   };
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!
52   static {
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);
57     }
58   }
59   public final static String RAINBOW_TYPE = "rainbow";
60   private final static String RAINBOW_TEMP_PREF = "RAINBOW_TEMP_";
61
62   @NotNull private final TextAttributesScheme myColorsScheme;
63   @NotNull private final Color[] myRainbowColors;
64
65   public RainbowHighlighter(@Nullable TextAttributesScheme colorsScheme) {
66     myColorsScheme = colorsScheme != null ? colorsScheme : EditorColorsManager.getInstance().getGlobalScheme();
67     myRainbowColors = generateColorSequence(myColorsScheme);
68   }
69
70   public static final HighlightInfoType RAINBOW_ELEMENT = new HighlightInfoType.HighlightInfoTypeImpl(HighlightSeverity.INFORMATION, DefaultLanguageHighlighterColors.CONSTANT);
71
72   public static boolean isRainbowEnabled() {
73     return Registry.is("editor.rainbow.identifiers", false);
74   }
75
76   public static void setRainbowEnabled(boolean enabled) {
77     Registry.get("editor.rainbow.identifiers").setValue(enabled);
78   }
79
80   public static int hashColor(@NotNull String name, int colorsCount) {
81     return Math.abs(StringHash.murmur(name, 0x55AA)) % colorsCount;
82   }
83
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;
88   }
89
90   @Contract(pure = true)
91   private static int indexOfMin(@NotNull int[] index2usage, int start, int end) {
92     int min = Integer.MAX_VALUE;
93     int minIndex = start;
94     for (int i = start; i < end; i++) {
95       int value = index2usage[i];
96       if (value < min) {
97         min = value;
98         minIndex = i;
99       }
100     }
101     return minIndex;
102   }
103
104   @NotNull
105   @Contract(pure = true)
106   private Color calculateForeground(int colorIndex) {
107     return myRainbowColors[colorIndex];
108   }
109
110   public int getColorsCount() {
111     return myRainbowColors.length;
112   }
113
114   @NotNull
115   private static Color[] generateColorSequence(@NotNull TextAttributesScheme colorsScheme) {
116     String colorDump = ApplicationManager.getApplication().isUnitTestMode()
117                        ? UNIT_TEST_COLORS
118                        : Registry.get("rainbow.highlighter.colors").asString();
119
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);
123     }
124
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()]);
128   }
129
130   @NotNull
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]);
136       keys[i] = key;
137     }
138     return keys;
139   }
140
141   public static boolean isRainbowTempKey(TextAttributesKey key) {
142     return key.getExternalName().startsWith(RAINBOW_TEMP_PREF);
143   }
144
145   public HighlightInfo getInfo(int colorIndex, @Nullable PsiElement id, @Nullable TextAttributesKey colorKey) {
146     return id == null ? null : getInfoBuilder(colorIndex, colorKey).range(id).create();
147   }
148
149   public HighlightInfo getInfo(int colorIndex, int start, int end, @Nullable TextAttributesKey colorKey) {
150     return getInfoBuilder(colorIndex, colorKey).range(start, end).create();
151   }
152
153   @NotNull
154   protected HighlightInfo.Builder getInfoBuilder(int colorIndex, @Nullable TextAttributesKey colorKey) {
155     if (colorKey == null) {
156       colorKey = DefaultLanguageHighlighterColors.LOCAL_VARIABLE;
157     }
158     return HighlightInfo
159       .newHighlightInfo(RAINBOW_ELEMENT)
160       .textAttributes(TextAttributes
161                         .fromFlyweight(myColorsScheme
162                                          .getAttributes(colorKey)
163                                          .getFlyweight()
164                                          .withForeground(calculateForeground(colorIndex))));
165   }
166 }