/* * 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.codeHighlighting; 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.application.ApplicationManager; import com.intellij.openapi.editor.DefaultLanguageHighlighterColors; import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.openapi.editor.colors.TextAttributesScheme; import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.options.SchemeMetaInfo; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.ui.ColorUtil; import com.intellij.ui.JBColor; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.awt.*; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; public class RainbowHighlighter { private static final JBColor[] RAINBOW_JB_COLORS_DEFAULT = { new JBColor(0x9b3b6a, 0x529d52), new JBColor(0x114d77, 0xbe7070), new JBColor(0xbc8650, 0x3d7676), new JBColor(0x005910, 0xbe9970), new JBColor(0xbc5150, 0x9d527c), }; public static final TextAttributesKey[] RAINBOW_COLOR_KEYS = new TextAttributesKey[RAINBOW_JB_COLORS_DEFAULT.length]; private static final int RAINBOW_COLORS_BETWEEN = 4; private static final String UNIT_TEST_COLORS = "#000001,#000002,#000003,#000004"; // Do not modify! private static final String INHERITED = "inherited"; static { for (int i = 0; i < RAINBOW_JB_COLORS_DEFAULT.length; ++i) { //noinspection deprecation RAINBOW_COLOR_KEYS[i] = TextAttributesKey.createTextAttributesKey("RAINBOW_COLOR" + i, createRainbowAttribute(RAINBOW_JB_COLORS_DEFAULT[i])); } } public final static String RAINBOW_TYPE = "rainbow"; private final static String RAINBOW_TEMP_PREF = "RAINBOW_TEMP_"; @SuppressWarnings("deprecation") public final static TextAttributesKey RAINBOW_ANCHOR = TextAttributesKey.createTextAttributesKey(RAINBOW_TYPE, new TextAttributes()); @SuppressWarnings("deprecation") public final static TextAttributesKey RAINBOW_GRADIENT_DEMO = TextAttributesKey.createTextAttributesKey("rainbow_demo", new TextAttributes()); public final static Boolean DEFAULT_RAINBOW_ON = Boolean.FALSE; @NotNull private final TextAttributesScheme myColorsScheme; @NotNull private final Color[] myRainbowColors; public RainbowHighlighter(@Nullable TextAttributesScheme colorsScheme) { myColorsScheme = colorsScheme != null ? colorsScheme : EditorColorsManager.getInstance().getGlobalScheme(); myRainbowColors = generateColorSequence(myColorsScheme); } public static final HighlightInfoType RAINBOW_ELEMENT = new HighlightInfoType.HighlightInfoTypeImpl(HighlightSeverity.INFORMATION, DefaultLanguageHighlighterColors.CONSTANT); @Nullable @Contract("_, null -> !null") public static Boolean isRainbowEnabled(@Nullable TextAttributesScheme colorsScheme, @Nullable Language language) { if (colorsScheme instanceof SchemeMetaInfo) { String value = ((SchemeMetaInfo)colorsScheme).getMetaProperties().getProperty(getKey(language), INHERITED); if (String.valueOf(true).equals(value)) return Boolean.TRUE; if (String.valueOf(false).equals(value)) return Boolean.FALSE; return language == null ? DEFAULT_RAINBOW_ON : null; } return false; } public static boolean isRainbowEnabledWithInheritance(@Nullable TextAttributesScheme colorsScheme, @Nullable Language language) { Boolean rainbowEnabled = isRainbowEnabled(colorsScheme, language); return rainbowEnabled != null ? rainbowEnabled : isRainbowEnabled(colorsScheme, null); } public static void setRainbowEnabled(@NotNull SchemeMetaInfo colorsScheme, @Nullable Language language, @Nullable Boolean enabled) { Properties properties = colorsScheme.getMetaProperties(); String key = getKey(language); if (enabled == null || (language == null && enabled == DEFAULT_RAINBOW_ON)) { properties.remove(key); } else { properties.setProperty(key, String.valueOf(enabled)); } } @NotNull private static String getKey(@Nullable Language language) { return RAINBOW_TYPE + " " + (language == null ? "Default language" : language.getID()); } @NotNull public static String generatePaletteExample() { int stopCount = RAINBOW_COLOR_KEYS.length; StringBuilder sb = new StringBuilder(); String tagRainbow = RAINBOW_GRADIENT_DEMO.getExternalName(); for (int i = 0; i < RAINBOW_TEMP_KEYS.length; ++i) { if (sb.length() != 0) { sb.append(" "); } sb.append("<").append(tagRainbow).append(">"); sb.append((i % stopCount == 0) ? "Stop#" + String.valueOf(i / stopCount + 1) : "T"); sb.append("</").append(tagRainbow).append(">"); } return sb.toString(); } @NotNull @Contract(pure = true) private Color calculateForeground(int colorIndex) { return myRainbowColors[colorIndex]; } public int getColorsCount() { return myRainbowColors.length; } @NotNull private static Color[] generateColorSequence(@NotNull TextAttributesScheme colorsScheme) { String colorDump = ApplicationManager.getApplication().isUnitTestMode() ? UNIT_TEST_COLORS : Registry.get("rainbow.highlighter.colors").asString(); final List<String> registryColors = StringUtil.split(colorDump, ","); if (!registryColors.isEmpty()) { return registryColors.stream().map(s -> ColorUtil.fromHex(s.trim())).toArray(Color[]::new); } List<Color> stopColors = ContainerUtil.map(RAINBOW_COLOR_KEYS, key -> colorsScheme.getAttributes(key).getForegroundColor()); List<Color> colors = ColorGenerator.generateLinearColorSequence(stopColors, RAINBOW_COLORS_BETWEEN); return colors.toArray(new Color[colors.size()]); } @NotNull public TextAttributesKey[] getRainbowTempKeys() { TextAttributesKey[] keys = new TextAttributesKey[myRainbowColors.length]; for (int i = 0; i < myRainbowColors.length; ++i) { //noinspection deprecation TextAttributesKey key = TextAttributesKey.createTextAttributesKey(RAINBOW_TEMP_PREF + i, new TextAttributes()); key.getDefaultAttributes().setForegroundColor(myRainbowColors[i]); keys[i] = key; } return keys; } public static boolean isRainbowTempKey(TextAttributesKey key) { return key.getExternalName().startsWith(RAINBOW_TEMP_PREF); } public HighlightInfo getInfo(int colorIndex, @Nullable PsiElement id, @Nullable TextAttributesKey colorKey) { return id == null ? null : getInfoBuilder(colorIndex, colorKey).range(id).create(); } public HighlightInfo getInfo(int colorIndex, int start, int end, @Nullable TextAttributesKey colorKey) { return getInfoBuilder(colorIndex, colorKey).range(start, end).create(); } @NotNull protected HighlightInfo.Builder getInfoBuilder(int colorIndex, @Nullable TextAttributesKey colorKey) { if (colorKey == null) { colorKey = DefaultLanguageHighlighterColors.LOCAL_VARIABLE; } return HighlightInfo .newHighlightInfo(RAINBOW_ELEMENT) .textAttributes(TextAttributes .fromFlyweight(myColorsScheme .getAttributes(colorKey) .getFlyweight() .withForeground(calculateForeground(colorIndex)))); } private static final TextAttributesKey[] RAINBOW_TEMP_KEYS = new RainbowHighlighter(null).getRainbowTempKeys(); @NotNull public static TextAttributes createRainbowAttribute(@Nullable Color color) { TextAttributes ret = new TextAttributes(); ret.setForegroundColor(color); return ret; } public static Map<String, TextAttributesKey> createRainbowHLM() { Map<String, TextAttributesKey> hashMap = new HashMap<>(); hashMap.put(RAINBOW_ANCHOR.getExternalName(), RAINBOW_ANCHOR); hashMap.put(RAINBOW_GRADIENT_DEMO.getExternalName(), RAINBOW_GRADIENT_DEMO); for (TextAttributesKey key : RAINBOW_TEMP_KEYS) { hashMap.put(key.getExternalName(), key); } return hashMap; } }