private void writeAttributes(Element attrElements) throws WriteExternalException {
    List<TextAttributesKey> list = new ArrayList<TextAttributesKey>(myAttributesMap.keySet());
    Collections.sort(list);

    for (TextAttributesKey key : list) {
      TextAttributes defaultAttr =
          myParentScheme != null ? myParentScheme.getAttributes(key) : new TextAttributes();
      TextAttributesKey baseKey = key.getFallbackAttributeKey();
      TextAttributes defaultFallbackAttr =
          baseKey != null && myParentScheme instanceof AbstractColorsScheme
              ? ((AbstractColorsScheme) myParentScheme).getFallbackAttributes(baseKey)
              : null;
      TextAttributes value = myAttributesMap.get(key);
      if (!value.equals(defaultAttr) || defaultAttr == defaultFallbackAttr) {
        Element element = new Element(OPTION_ELEMENT);
        element.setAttribute(NAME_ATTR, key.getExternalName());
        if (baseKey == null || !value.isFallbackEnabled()) {
          Element valueElement = new Element(VALUE_ELEMENT);
          value.writeExternal(valueElement);
          element.addContent(valueElement);
          attrElements.addContent(element);
        } else if (defaultAttr != defaultFallbackAttr) {
          element.setAttribute(BASE_ATTRIBUTES_ATTR, baseKey.getExternalName());
          attrElements.addContent(element);
        }
      }
    }
  }
 private SchemeTextAttributesDescription(
     String name,
     String group,
     @NotNull TextAttributesKey key,
     @NotNull MyColorScheme scheme,
     Icon icon,
     String toolTip) {
   super(name, group, getInitialAttributes(scheme, key).clone(), key, scheme, icon, toolTip);
   this.key = key;
   myAttributesToApply = getInitialAttributes(scheme, key);
   TextAttributesKey fallbackKey = key.getFallbackAttributeKey();
   if (fallbackKey != null) {
     myFallbackAttributes = scheme.getAttributes(fallbackKey);
     myBaseAttributeDescriptor =
         ColorSettingsPages.getInstance().getAttributeDescriptor(fallbackKey);
     if (myBaseAttributeDescriptor == null) {
       myBaseAttributeDescriptor =
           new Pair<ColorSettingsPage, AttributesDescriptor>(
               null, new AttributesDescriptor(fallbackKey.getExternalName(), fallbackKey));
     }
   }
   myIsInheritedInitial = isInherited(scheme);
   setInherited(myIsInheritedInitial);
   initCheckedStatus();
 }
 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;
 }
 protected TextAttributes getFallbackAttributes(TextAttributesKey fallbackKey) {
   if (fallbackKey == null) return null;
   TextAttributes fallbackAttributes = getDirectlyDefinedAttributes(fallbackKey);
   if (fallbackAttributes != null) {
     if (!fallbackAttributes.isFallbackEnabled()
         || fallbackKey.getFallbackAttributeKey() == null) {
       return fallbackAttributes;
     }
   }
   return getFallbackAttributes(fallbackKey.getFallbackAttributeKey());
 }
 @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;
 }
/** @author Robert F. Beeger ([email protected]) */
public class ManifestColorsAndFonts {
  public static final TextAttributesKey HEADER_NAME_KEY =
      TextAttributesKey.createTextAttributesKey(
          "manifest.header.name", DefaultLanguageHighlighterColors.KEYWORD);
  public static final TextAttributesKey HEADER_ASSIGNMENT_KEY =
      TextAttributesKey.createTextAttributesKey(
          "manifest.header.assignment", DefaultLanguageHighlighterColors.OPERATION_SIGN);
  public static final TextAttributesKey HEADER_VALUE_KEY =
      TextAttributesKey.createTextAttributesKey(
          "manifest.header.value", DefaultLanguageHighlighterColors.IDENTIFIER);

  private ManifestColorsAndFonts() {}
}
 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]));
   }
 }
  private void migrateErrorStripeColorFrom14(
      @NotNull TextAttributesKey name, @NotNull TextAttributes attr) {
    if (myVersion >= 141 || myParentScheme == null) return;

    Couple<Color> m = DEFAULT_STRIPE_COLORS.get(name.getExternalName());
    if (m != null && Comparing.equal(m.first, attr.getErrorStripeColor())) {
      attr.setErrorStripeColor(m.second);
    }
  }
 public void readAttributes(@NotNull Element childNode) {
   for (Element e : childNode.getChildren(OPTION_ELEMENT)) {
     TextAttributesKey name = TextAttributesKey.find(e.getAttributeValue(NAME_ATTR));
     Element valueElement = e.getChild(VALUE_ELEMENT);
     TextAttributes attr =
         valueElement != null ? new TextAttributes(valueElement) : new TextAttributes();
     myAttributesMap.put(name, attr);
     migrateErrorStripeColorFrom14(name, attr);
   }
 }
 private boolean isInherited(@NotNull MyColorScheme scheme) {
   TextAttributes attributes = scheme.getAttributes(key);
   TextAttributesKey fallbackKey = key.getFallbackAttributeKey();
   if (fallbackKey != null && !scheme.containsKey(key)) {
     TextAttributes fallbackAttributes = scheme.getAttributes(fallbackKey);
     if (attributes != null && attributes == fallbackAttributes) {
       return true;
     }
   }
   return false;
 }
 @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();
 }
  @Nullable
  public Map<String, TextAttributesKey> getAdditionalHighlightingTagToDescriptorMap() {
    final Map<String, TextAttributesKey> map = new HashMap<String, TextAttributesKey>();
    map.put("string", BashSyntaxHighlighter.STRING);
    map.put("heredoc", BashSyntaxHighlighter.HERE_DOC);
    map.put("heredocStart", BashSyntaxHighlighter.HERE_DOC_START);
    map.put("heredocEnd", BashSyntaxHighlighter.HERE_DOC_END);
    map.put("backquote", BashSyntaxHighlighter.BACKQUOTE);
    map.put("internalCmd", BashSyntaxHighlighter.INTERNAL_COMMAND);
    map.put("externalCmd", BashSyntaxHighlighter.EXTERNAL_COMMAND);
    map.put("subshellCmd", BashSyntaxHighlighter.SUBSHELL_COMMAND);
    map.put("functionCall", BashSyntaxHighlighter.FUNCTION_CALL);
    map.put("varDef", BashSyntaxHighlighter.VAR_DEF);
    map.put("internalVar", BashSyntaxHighlighter.VAR_USE_BUILTIN);
    map.put("composedVar", BashSyntaxHighlighter.VAR_USE_COMPOSED);

    // we need this to be able to insert << in the text
    // (relies on the current implementation of JetBrain's HighlightsExtractor class)
    map.put("dummy", TextAttributesKey.find("dummy"));
    return map;
  }
/** @author ilyas */
public class DefaultHighlighter {

  @NonNls static final String LINE_COMMENT_ID = "Line comment";
  @NonNls static final String BLOCK_COMMENT_ID = "Block comment";
  @NonNls static final String DOC_COMMENT_ID = "Groovydoc comment";
  @NonNls static final String DOC_COMMENT_TAG_ID = "Groovydoc tag";
  @NonNls static final String KEYWORD_ID = "Keyword";
  @NonNls static final String NUMBER_ID = "Number";
  @NonNls static final String GSTRING_ID = "GString";
  @NonNls static final String STRING_ID = "String";
  @NonNls static final String BRACES_ID = "Braces";
  @NonNls static final String BRACKETS_ID = "Brackets";
  @NonNls static final String PARENTHESES_ID = "Parentheses";

  @NonNls static final String OPERATION_SIGN_ID = "Operation sign";
  @NonNls static final String BAD_CHARACTER_ID = "Bad character";
  @NonNls static final String WRONG_STRING_ID = "Wrong string literal";

  @NonNls static final String ANNOTATION_ID = "Annotation";
  @NonNls static final String INSTANCE_FIELD_ID = "Instance field";
  @NonNls static final String STATIC_FIELD_ID = "Static field";
  @NonNls static final String METHOD_CALL_ID = "Method call";
  @NonNls static final String STATIC_METHOD_ACCESS_ID = "Static method access";
  @NonNls static final String CLASS_REFERENCE_ID = "Class";
  @NonNls static final String TYPE_PARAMETER_ID = "Type parameter";
  @NonNls static final String INSTANCE_PROPERTY_REFERENCE_ID = "Instance property reference ID";
  @NonNls static final String STATIC_PROPERTY_REFERENCE_ID = "Static property reference ID";
  @NonNls static final String MAP_KEY_ID = "Map key";

  @NonNls static final String UNRESOLVED_ACCESS_ID = "Unresolved reference access";
  static final String LITERAL_CONVERSION_ID = "List/map to object conversion";

  static final String VALID_STRING_ESCAPE_ID = "Valid string escape";
  static final String INVALID_STRING_ESCAPE_ID = "Invalid string escape";

  @NonNls static final String LABEL_ID = "Label";

  public static TextAttributesKey LINE_COMMENT =
      TextAttributesKey.createTextAttributesKey(
          LINE_COMMENT_ID, SyntaxHighlighterColors.LINE_COMMENT.getDefaultAttributes());

  public static TextAttributesKey ANNOTATION =
      TextAttributesKey.createTextAttributesKey(
          ANNOTATION_ID,
          HighlightInfoType.ANNOTATION_NAME.getAttributesKey().getDefaultAttributes());

  public static TextAttributesKey LOCAL_VARIABLE =
      TextAttributesKey.createTextAttributesKey(
          "Groovy var", HighlightInfoType.LOCAL_VARIABLE.getAttributesKey().getDefaultAttributes());
  public static TextAttributesKey REASSIGNED_LOCAL_VARIABLE =
      TextAttributesKey.createTextAttributesKey(
          "Groovy reassigned var",
          HighlightInfoType.REASSIGNED_LOCAL_VARIABLE.getAttributesKey().getDefaultAttributes());
  public static TextAttributesKey PARAMETER =
      TextAttributesKey.createTextAttributesKey(
          "Groovy parameter",
          HighlightInfoType.PARAMETER.getAttributesKey().getDefaultAttributes());
  public static TextAttributesKey REASSIGNED_PARAMETER =
      TextAttributesKey.createTextAttributesKey(
          "Groovy reassigned parameter",
          HighlightInfoType.REASSIGNED_PARAMETER.getAttributesKey().getDefaultAttributes());

  public static TextAttributesKey METHOD_DECLARATION =
      TextAttributesKey.createTextAttributesKey(
          "Groovy method declaration",
          HighlightInfoType.METHOD_DECLARATION.getAttributesKey().getDefaultAttributes());

  public static TextAttributesKey INSTANCE_FIELD =
      TextAttributesKey.createTextAttributesKey(
          INSTANCE_FIELD_ID,
          HighlightInfoType.INSTANCE_FIELD.getAttributesKey().getDefaultAttributes());

  public static TextAttributesKey METHOD_CALL =
      TextAttributesKey.createTextAttributesKey(
          METHOD_CALL_ID, HighlightInfoType.METHOD_CALL.getAttributesKey().getDefaultAttributes());

  public static TextAttributesKey STATIC_FIELD =
      TextAttributesKey.createTextAttributesKey(
          STATIC_FIELD_ID,
          HighlightInfoType.STATIC_FINAL_FIELD.getAttributesKey().getDefaultAttributes());

  public static TextAttributesKey STATIC_METHOD_ACCESS =
      TextAttributesKey.createTextAttributesKey(
          STATIC_METHOD_ACCESS_ID,
          HighlightInfoType.STATIC_METHOD.getAttributesKey().getDefaultAttributes());

  public static TextAttributesKey BLOCK_COMMENT =
      TextAttributesKey.createTextAttributesKey(
          BLOCK_COMMENT_ID, SyntaxHighlighterColors.JAVA_BLOCK_COMMENT.getDefaultAttributes());

  public static TextAttributesKey DOC_COMMENT_CONTENT =
      TextAttributesKey.createTextAttributesKey(
          DOC_COMMENT_ID, SyntaxHighlighterColors.DOC_COMMENT.getDefaultAttributes());

  public static TextAttributesKey DOC_COMMENT_TAG =
      TextAttributesKey.createTextAttributesKey(
          DOC_COMMENT_TAG_ID, SyntaxHighlighterColors.DOC_COMMENT_TAG.getDefaultAttributes());

  public static TextAttributesKey CLASS_REFERENCE =
      TextAttributesKey.createTextAttributesKey(
          CLASS_REFERENCE_ID, HighlighterColors.TEXT.getDefaultAttributes().clone());

  public static TextAttributesKey TYPE_PARAMETER =
      TextAttributesKey.createTextAttributesKey(
          TYPE_PARAMETER_ID,
          CodeInsightColors.TYPE_PARAMETER_NAME_ATTRIBUTES.getDefaultAttributes().clone());

  public static final TextAttributes INSTANCE_PROPERTY_REFERENCE_ATTRIBUTES =
      INSTANCE_FIELD.getDefaultAttributes().clone();
  public static final TextAttributes STATIC_PROPERTY_REFERENCE_ATTRIBUTES =
      STATIC_FIELD.getDefaultAttributes().clone();

  static {
    INSTANCE_PROPERTY_REFERENCE_ATTRIBUTES.setFontType(Font.PLAIN);
    STATIC_PROPERTY_REFERENCE_ATTRIBUTES.setFontType(Font.ITALIC);
  }

  public static TextAttributesKey INSTANCE_PROPERTY_REFERENCE =
      TextAttributesKey.createTextAttributesKey(
          INSTANCE_PROPERTY_REFERENCE_ID, INSTANCE_PROPERTY_REFERENCE_ATTRIBUTES);

  public static TextAttributesKey STATIC_PROPERTY_REFERENCE =
      TextAttributesKey.createTextAttributesKey(
          STATIC_PROPERTY_REFERENCE_ID, STATIC_PROPERTY_REFERENCE_ATTRIBUTES);

  public static final TextAttributes KEYWORD_ATTRIBUTES =
      SyntaxHighlighterColors.KEYWORD.getDefaultAttributes().clone();

  static {
    KEYWORD_ATTRIBUTES.setForegroundColor(new Color(0, 0, 67));
    KEYWORD_ATTRIBUTES.setFontType(Font.BOLD);
  }

  public static TextAttributesKey KEYWORD =
      TextAttributesKey.createTextAttributesKey(
          "GROOVY_" + KEYWORD_ID.toUpperCase(), KEYWORD_ATTRIBUTES);

  public static TextAttributesKey NUMBER =
      TextAttributesKey.createTextAttributesKey(
          NUMBER_ID, SyntaxHighlighterColors.NUMBER.getDefaultAttributes());

  public static TextAttributesKey GSTRING =
      TextAttributesKey.createTextAttributesKey(
          GSTRING_ID, SyntaxHighlighterColors.STRING.getDefaultAttributes());

  public static TextAttributesKey STRING =
      TextAttributesKey.createTextAttributesKey(
          STRING_ID, SyntaxHighlighterColors.STRING.getDefaultAttributes());

  public static TextAttributesKey BRACES =
      TextAttributesKey.createTextAttributesKey(
          BRACES_ID, SyntaxHighlighterColors.BRACES.getDefaultAttributes());

  public static TextAttributesKey BRACKETS =
      TextAttributesKey.createTextAttributesKey(
          BRACKETS_ID, SyntaxHighlighterColors.BRACKETS.getDefaultAttributes());

  public static TextAttributesKey PARENTHESES =
      TextAttributesKey.createTextAttributesKey(
          PARENTHESES_ID, SyntaxHighlighterColors.PARENTHS.getDefaultAttributes());

  public static TextAttributesKey OPERATION_SIGN =
      TextAttributesKey.createTextAttributesKey(
          OPERATION_SIGN_ID, SyntaxHighlighterColors.OPERATION_SIGN.getDefaultAttributes());

  public static TextAttributesKey BAD_CHARACTER =
      TextAttributesKey.createTextAttributesKey(
          BAD_CHARACTER_ID, CodeInsightColors.UNMATCHED_BRACE_ATTRIBUTES.getDefaultAttributes());

  public static TextAttributesKey WRONG_STRING =
      TextAttributesKey.createTextAttributesKey(
          WRONG_STRING_ID, SyntaxHighlighterColors.STRING.getDefaultAttributes());

  public static final TextAttributes UNRESOLVED_ACCESS_ATTRIBUTES =
      HighlighterColors.TEXT.getDefaultAttributes().clone();

  static {
    UNRESOLVED_ACCESS_ATTRIBUTES.setForegroundColor(Color.BLACK);
    UNRESOLVED_ACCESS_ATTRIBUTES.setEffectColor(Color.GRAY);
    UNRESOLVED_ACCESS_ATTRIBUTES.setEffectType(EffectType.LINE_UNDERSCORE);
  }

  public static final TextAttributes LITERAL_CONVERSION_ATTRIBUTES =
      HighlighterColors.TEXT.getDefaultAttributes().clone();

  static {
    LITERAL_CONVERSION_ATTRIBUTES.setForegroundColor(PlatformColors.BLUE);
    LITERAL_CONVERSION_ATTRIBUTES.setFontType(Font.BOLD);
  }

  public static final TextAttributes MAP_KEY_ATTRIBUTES =
      HighlighterColors.TEXT.getDefaultAttributes().clone();

  public static final Color MAP_KEY_COLOR = new Color(0, 128, 0);

  static {
    MAP_KEY_ATTRIBUTES.setForegroundColor(MAP_KEY_COLOR);
  }

  public static TextAttributesKey UNRESOLVED_ACCESS =
      TextAttributesKey.createTextAttributesKey(UNRESOLVED_ACCESS_ID, UNRESOLVED_ACCESS_ATTRIBUTES);
  public static TextAttributesKey LITERAL_CONVERSION =
      TextAttributesKey.createTextAttributesKey(
          LITERAL_CONVERSION_ID, LITERAL_CONVERSION_ATTRIBUTES);

  public static TextAttributesKey MAP_KEY =
      TextAttributesKey.createTextAttributesKey(MAP_KEY_ID, MAP_KEY_ATTRIBUTES);

  public static final TextAttributesKey VALID_STRING_ESCAPE =
      TextAttributesKey.createTextAttributesKey(
          VALID_STRING_ESCAPE_ID,
          SyntaxHighlighterColors.VALID_STRING_ESCAPE.getDefaultAttributes());
  public static final TextAttributesKey INVALID_STRING_ESCAPE =
      TextAttributesKey.createTextAttributesKey(
          INVALID_STRING_ESCAPE_ID,
          SyntaxHighlighterColors.INVALID_STRING_ESCAPE.getDefaultAttributes());

  public static final TextAttributes LABEL_ATTRIBUTES =
      HighlighterColors.TEXT.getDefaultAttributes().clone();

  public static final TextAttributesKey LABEL =
      TextAttributesKey.createTextAttributesKey(LABEL_ID, LABEL_ATTRIBUTES);

  private DefaultHighlighter() {}
}
/** Created by IntelliJ IDEA. User: jon Date: Apr 3, 2010 Time: 1:55:00 AM */
public class MoonHighlightingData {
  @NonNls static final String KEYWORD_ID = "MOON_KEYWORD";
  @NonNls static final String COMMENT_ID = "MOON_COMMENT";
  @NonNls static final String LONGCOMMENT_ID = "MOON_LONGCOMMENT";
  @NonNls static final String NUMBER_ID = "MOON_NUMBER";
  @NonNls static final String STRING_ID = "MOON_STRING";
  @NonNls static final String LONGSTRING_ID = "MOON_LONGSTRING";
  @NonNls static final String LONGSTRING_BRACES_ID = "MOON_LONGSTRING_BRACES";
  @NonNls static final String LONGCOMMENT_BRACES_ID = "MOON_LONGCOMMENT_BRACES";
  @NonNls static final String BRACES_ID = "MOON_BRACES";
  @NonNls static final String PARENTHS_ID = "MOON_PARENTHS";
  @NonNls static final String BRACKETS_ID = "MOON_BRACKETS";
  @NonNls static final String BAD_CHARACTER_ID = "MOON_BAD_CHARACTER";
  @NonNls static final String IDENTIFIER_ID = "MOON_IDENTIFIER";
  @NonNls static final String VAR_ID = "MOON_VAR";
  @NonNls static final String COMMA_ID = "MOON_COMMA";
  @NonNls static final String SEMICOLON_ID = "MOON_SEMICOLON";
  @NonNls static final String SELF_ID = "MOON_SELF";
  @NonNls static final String DEFINED_CONSTANTS_ID = "MOON_DEFINED_CONSTANTS";
  @NonNls static final String LOCAL_VAR_ID = "MOON_LOCAL_VAR";
  @NonNls static final String GLOBAL_VAR_ID = "MOON_GLOBAL_VAR";
  @NonNls static final String FIELD_ID = "MOON_FIELD";
  @NonNls static final String TAIL_CALL_ID = "MOON_TAIL_CALL";
  @NonNls static final String PARAMETER_ID = "MOON_PARAMETER";
  @NonNls static final String UPVAL_ID = "MOON_UPVAL";

  @NonNls static final String LUADOC_ID = "MOON_LUADOC";
  @NonNls static final String LUADOC_TAG_ID = "MOON_LUADOC_TAG";
  @NonNls static final String LUADOC_VALUE_ID = "MOON_LUADOC_VALUE";

  @NonNls static final String OPERATION_SIGN_ID = "MOON_OPERATION_SIGN";

  private static final TextAttributes LUADOC_ATTR =
      SyntaxHighlighterColors.DOC_COMMENT.getDefaultAttributes().clone();
  public static final TextAttributesKey LUADOC =
      TextAttributesKey.createTextAttributesKey(LUADOC_ID, LUADOC_ATTR);

  private static TextAttributes LUADOC_TAG_ATTR =
      SyntaxHighlighterColors.DOC_COMMENT_TAG.getDefaultAttributes().clone();
  public static TextAttributesKey LUADOC_TAG =
      TextAttributesKey.createTextAttributesKey(LUADOC_TAG_ID, LUADOC_TAG_ATTR);

  private static TextAttributes LUADOC_VALUE_ATTR =
      SyntaxHighlighterColors.DOC_COMMENT.getDefaultAttributes().clone();
  public static TextAttributesKey LUADOC_VALUE =
      TextAttributesKey.createTextAttributesKey(LUADOC_VALUE_ID, LUADOC_VALUE_ATTR);

  private static final TextAttributes UPVAL_ATTR =
      HighlighterColors.TEXT.getDefaultAttributes().clone();
  public static final TextAttributesKey UPVAL =
      TextAttributesKey.createTextAttributesKey(UPVAL_ID, UPVAL_ATTR);

  private static final TextAttributes LOCAL_VAR_ATTR =
      HighlighterColors.TEXT.getDefaultAttributes().clone();
  public static final TextAttributesKey LOCAL_VAR =
      TextAttributesKey.createTextAttributesKey(LOCAL_VAR_ID, LOCAL_VAR_ATTR);

  private static final TextAttributes PARAMETER_ATTR =
      HighlighterColors.TEXT.getDefaultAttributes().clone();
  public static final TextAttributesKey PARAMETER =
      TextAttributesKey.createTextAttributesKey(PARAMETER_ID, PARAMETER_ATTR);

  private static final TextAttributes GLOBAL_VAR_ATTR =
      HighlighterColors.TEXT.getDefaultAttributes().clone();
  public static final TextAttributesKey GLOBAL_VAR =
      TextAttributesKey.createTextAttributesKey(GLOBAL_VAR_ID, GLOBAL_VAR_ATTR);

  public static final TextAttributesKey FIELD =
      TextAttributesKey.createTextAttributesKey(
          FIELD_ID, HighlighterColors.TEXT.getDefaultAttributes().clone());

  static final TextAttributes TAIL_CALL_ATTR =
      HighlighterColors.TEXT.getDefaultAttributes().clone();

  public static final TextAttributesKey TAIL_CALL =
      TextAttributesKey.createTextAttributesKey(TAIL_CALL_ID, TAIL_CALL_ATTR);

  public static final TextAttributesKey KEYWORD =
      TextAttributesKey.createTextAttributesKey(
          KEYWORD_ID, SyntaxHighlighterColors.KEYWORD.getDefaultAttributes().clone());

  public static final TextAttributesKey COMMENT =
      TextAttributesKey.createTextAttributesKey(
          COMMENT_ID, SyntaxHighlighterColors.LINE_COMMENT.getDefaultAttributes().clone());
  public static final TextAttributesKey LONGCOMMENT =
      TextAttributesKey.createTextAttributesKey(
          LONGCOMMENT_ID,
          SyntaxHighlighterColors.JAVA_BLOCK_COMMENT.getDefaultAttributes().clone());

  static final TextAttributes LONGCOMMENT_BRACES_ATTR =
      SyntaxHighlighterColors.JAVA_BLOCK_COMMENT.getDefaultAttributes().clone();

  public static final TextAttributesKey LONGCOMMENT_BRACES =
      TextAttributesKey.createTextAttributesKey(LONGCOMMENT_BRACES_ID, LONGCOMMENT_BRACES_ATTR);

  public static final TextAttributesKey NUMBER =
      TextAttributesKey.createTextAttributesKey(
          NUMBER_ID, SyntaxHighlighterColors.NUMBER.getDefaultAttributes().clone());
  public static final TextAttributesKey STRING =
      TextAttributesKey.createTextAttributesKey(
          STRING_ID, SyntaxHighlighterColors.STRING.getDefaultAttributes().clone());

  private static final TextAttributes LONGSTRING_ATTR =
      SyntaxHighlighterColors.STRING.getDefaultAttributes().clone();
  private static final TextAttributes LONGSTRING_BRACES_ATTR =
      SyntaxHighlighterColors.STRING.getDefaultAttributes().clone();
  public static final TextAttributesKey LONGSTRING =
      TextAttributesKey.createTextAttributesKey(LONGSTRING_ID, LONGSTRING_ATTR);
  public static final TextAttributesKey LONGSTRING_BRACES =
      TextAttributesKey.createTextAttributesKey(LONGSTRING_BRACES_ID, LONGSTRING_BRACES_ATTR);
  public static final TextAttributesKey BRACKETS =
      TextAttributesKey.createTextAttributesKey(
          BRACKETS_ID, SyntaxHighlighterColors.BRACKETS.getDefaultAttributes().clone());
  public static final TextAttributesKey BRACES =
      TextAttributesKey.createTextAttributesKey(
          BRACES_ID, SyntaxHighlighterColors.BRACES.getDefaultAttributes().clone());
  public static final TextAttributesKey PARENTHS =
      TextAttributesKey.createTextAttributesKey(
          PARENTHS_ID, SyntaxHighlighterColors.PARENTHS.getDefaultAttributes().clone());
  public static final TextAttributesKey BAD_CHARACTER =
      TextAttributesKey.createTextAttributesKey(
          BAD_CHARACTER_ID, HighlighterColors.BAD_CHARACTER.getDefaultAttributes().clone());

  public static final TextAttributesKey COMMA =
      TextAttributesKey.createTextAttributesKey(
          COMMA_ID, SyntaxHighlighterColors.COMMA.getDefaultAttributes().clone());

  private static final TextAttributes DEFINED_CONSTANTS_ATTR =
      SyntaxHighlighterColors.KEYWORD.getDefaultAttributes().clone();

  public static final TextAttributesKey DEFINED_CONSTANTS =
      TextAttributesKey.createTextAttributesKey(DEFINED_CONSTANTS_ID, DEFINED_CONSTANTS_ATTR);

  public static final TextAttributesKey OPERATION_SIGN =
      TextAttributesKey.createTextAttributesKey(
          OPERATION_SIGN_ID, SyntaxHighlighterColors.OPERATION_SIGN.getDefaultAttributes().clone());

  static {
    DEFINED_CONSTANTS_ATTR.setForegroundColor(Color.MAGENTA);
    UPVAL_ATTR.setFontType(SimpleTextAttributes.STYLE_ITALIC);
    LONGSTRING_ATTR.setBackgroundColor(new Color(0xD0, 0xD0, 0xD0));
    GLOBAL_VAR_ATTR.setForegroundColor(new Color(128, 0, 0));
    LOCAL_VAR_ATTR.setForegroundColor(new Color(0, 153, 153));
    PARAMETER_ATTR.setForegroundColor(new Color(153, 102, 255));
    LUADOC_ATTR.setForegroundColor(new Color(64, 95, 189));
    LUADOC_TAG_ATTR.setForegroundColor(new Color(64, 95, 189));
    LUADOC_VALUE_ATTR.setForegroundColor(new Color(64, 95, 189));
  }
}
public class CtrlMouseHandler extends AbstractProjectComponent {
  private final TextAttributes ourReferenceAttributes;
  private HighlightersSet myHighlighter;
  private int myStoredModifiers = 0;
  private TooltipProvider myTooltipProvider = null;
  private final FileEditorManager myFileEditorManager;

  private enum BrowseMode {
    None,
    Declaration,
    TypeDeclaration,
    Implementation
  }

  private final KeyListener myEditorKeyListener =
      new KeyAdapter() {
        public void keyPressed(final KeyEvent e) {
          handleKey(e);
        }

        public void keyReleased(final KeyEvent e) {
          handleKey(e);
        }

        private void handleKey(final KeyEvent e) {
          int modifiers = e.getModifiers();
          if (modifiers == myStoredModifiers) {
            return;
          }

          BrowseMode browseMode = getBrowseMode(modifiers);

          if (browseMode != BrowseMode.None) {
            if (myTooltipProvider != null) {
              if (browseMode != myTooltipProvider.getBrowseMode()) {
                disposeHighlighter();
              }
              myStoredModifiers = modifiers;
              myTooltipProvider.execute(browseMode);
            }
          } else {
            disposeHighlighter();
            myTooltipProvider = null;
          }
        }
      };

  private final FileEditorManagerListener myFileEditorManagerListener =
      new FileEditorManagerAdapter() {
        public void selectionChanged(FileEditorManagerEvent e) {
          disposeHighlighter();
          myTooltipProvider = null;
        }
      };

  private final VisibleAreaListener myVisibleAreaListener =
      new VisibleAreaListener() {
        public void visibleAreaChanged(VisibleAreaEvent e) {
          disposeHighlighter();
          myTooltipProvider = null;
        }
      };

  private final EditorMouseAdapter myEditorMouseAdapter =
      new EditorMouseAdapter() {
        public void mouseReleased(EditorMouseEvent e) {
          disposeHighlighter();
          myTooltipProvider = null;
        }
      };

  private final EditorMouseMotionListener myEditorMouseMotionListener =
      new EditorMouseMotionAdapter() {
        public void mouseMoved(final EditorMouseEvent e) {
          if (e.isConsumed() || !myProject.isInitialized()) {
            return;
          }
          MouseEvent mouseEvent = e.getMouseEvent();

          Editor editor = e.getEditor();
          if (editor.getProject() != null && editor.getProject() != myProject) return;
          PsiFile psiFile =
              PsiDocumentManager.getInstance(myProject).getPsiFile(editor.getDocument());
          Point point = new Point(mouseEvent.getPoint());
          if (!PsiDocumentManager.getInstance(myProject).isUncommited(editor.getDocument())) {
            // when document is committed, try to check injected stuff - it's fast
            editor =
                InjectedLanguageUtil.getEditorForInjectedLanguageNoCommit(
                    editor,
                    psiFile,
                    editor.logicalPositionToOffset(editor.xyToLogicalPosition(point)));
          }

          LogicalPosition pos = editor.xyToLogicalPosition(point);
          int offset = editor.logicalPositionToOffset(pos);
          int selStart = editor.getSelectionModel().getSelectionStart();
          int selEnd = editor.getSelectionModel().getSelectionEnd();

          myStoredModifiers = mouseEvent.getModifiers();
          BrowseMode browseMode = getBrowseMode(myStoredModifiers);

          if (myTooltipProvider != null) {
            myTooltipProvider.dispose();
          }

          if (browseMode == BrowseMode.None || offset >= selStart && offset < selEnd) {
            disposeHighlighter();
            myTooltipProvider = null;
            return;
          }

          myTooltipProvider = new TooltipProvider(editor, pos);
          myTooltipProvider.execute(browseMode);
        }
      };

  private static final TextAttributesKey CTRL_CLICKABLE_ATTRIBUTES_KEY =
      TextAttributesKey.createTextAttributesKey(
          "CTRL_CLICKABLE",
          new TextAttributes(Color.blue, null, Color.blue, EffectType.LINE_UNDERSCORE, 0));

  public CtrlMouseHandler(
      final Project project,
      StartupManager startupManager,
      EditorColorsManager colorsManager,
      FileEditorManager fileEditorManager) {
    super(project);
    startupManager.registerPostStartupActivity(
        new DumbAwareRunnable() {
          public void run() {
            EditorEventMulticaster eventMulticaster =
                EditorFactory.getInstance().getEventMulticaster();
            eventMulticaster.addEditorMouseListener(myEditorMouseAdapter, project);
            eventMulticaster.addEditorMouseMotionListener(myEditorMouseMotionListener, project);
          }
        });
    ourReferenceAttributes =
        colorsManager.getGlobalScheme().getAttributes(CTRL_CLICKABLE_ATTRIBUTES_KEY);
    myFileEditorManager = fileEditorManager;
  }

  @NotNull
  public String getComponentName() {
    return "CtrlMouseHandler";
  }

  private static BrowseMode getBrowseMode(final int modifiers) {
    if (modifiers != 0) {
      final Keymap activeKeymap = KeymapManager.getInstance().getActiveKeymap();
      if (matchMouseShortcut(activeKeymap, modifiers, IdeActions.ACTION_GOTO_DECLARATION))
        return BrowseMode.Declaration;
      if (matchMouseShortcut(activeKeymap, modifiers, IdeActions.ACTION_GOTO_TYPE_DECLARATION))
        return BrowseMode.TypeDeclaration;
      if (matchMouseShortcut(activeKeymap, modifiers, IdeActions.ACTION_GOTO_IMPLEMENTATION))
        return BrowseMode.Implementation;
      if (modifiers == InputEvent.CTRL_MASK) return BrowseMode.Declaration;
    }
    return BrowseMode.None;
  }

  private static boolean matchMouseShortcut(
      final Keymap activeKeymap, final int modifiers, final String actionId) {
    final MouseShortcut syntheticShortcut = new MouseShortcut(MouseEvent.BUTTON1, modifiers, 1);
    for (Shortcut shortcut : activeKeymap.getShortcuts(actionId)) {
      if (shortcut instanceof MouseShortcut) {
        final MouseShortcut mouseShortcut = (MouseShortcut) shortcut;
        if (mouseShortcut.getModifiers() == syntheticShortcut.getModifiers()) {
          return true;
        }
      }
    }
    return false;
  }

  @TestOnly
  public static String getInfo(PsiElement element, PsiElement atPointer) {
    return generateInfo(element, atPointer);
  }

  @Nullable
  private static String generateInfo(PsiElement element, PsiElement atPointer) {
    final DocumentationProvider documentationProvider =
        DocumentationManager.getProviderFromElement(element, atPointer);

    String info = documentationProvider.getQuickNavigateInfo(element, atPointer);
    if (info != null) {
      return info;
    }

    if (element instanceof PsiFile) {
      final VirtualFile virtualFile = ((PsiFile) element).getVirtualFile();
      if (virtualFile != null) {
        return virtualFile.getPresentableUrl();
      }
    }

    info = getQuickNavigateInfo(element);
    if (info != null) {
      return info;
    }

    if (element instanceof NavigationItem) {
      final ItemPresentation presentation = ((NavigationItem) element).getPresentation();
      if (presentation != null) {
        return presentation.getPresentableText();
      }
    }

    return null;
  }

  @Nullable
  private static String getQuickNavigateInfo(PsiElement element) {
    final String name =
        ElementDescriptionUtil.getElementDescription(element, UsageViewShortNameLocation.INSTANCE);
    if (StringUtil.isEmpty(name)) return null;
    final String typeName =
        ElementDescriptionUtil.getElementDescription(element, UsageViewTypeLocation.INSTANCE);
    final PsiFile file = element.getContainingFile();
    final StringBuilder sb = new StringBuilder();
    if (StringUtil.isNotEmpty(typeName)) sb.append(typeName).append(" ");
    sb.append("\"").append(name).append("\"");
    if (file != null && file.isPhysical()) {
      sb.append(" [").append(file.getName()).append("]");
    }
    return sb.toString();
  }

  private abstract static class Info {
    @NotNull protected final PsiElement myElementAtPointer;
    private final List<TextRange> myRanges;

    public Info(@NotNull PsiElement elementAtPointer, List<TextRange> ranges) {
      myElementAtPointer = elementAtPointer;
      myRanges = ranges;
    }

    public Info(@NotNull PsiElement elementAtPointer) {
      this(
          elementAtPointer,
          Collections.singletonList(
              new TextRange(
                  elementAtPointer.getTextOffset(),
                  elementAtPointer.getTextOffset() + elementAtPointer.getTextLength())));
    }

    boolean isSimilarTo(final Info that) {
      return Comparing.equal(myElementAtPointer, that.myElementAtPointer)
          && myRanges.equals(that.myRanges);
    }

    public List<TextRange> getRanges() {
      return myRanges;
    }

    @Nullable
    public abstract String getInfo();

    public abstract boolean isValid(Document document);

    protected boolean rangesAreCorrect(Document document) {
      final TextRange docRange = new TextRange(0, document.getTextLength());
      for (TextRange range : getRanges()) {
        if (!docRange.contains(range)) return false;
      }

      return true;
    }
  }

  private static void showDumbModeNotification(final Project project) {
    DumbService.getInstance(project)
        .showDumbModeNotification("Element information is not available during index update");
  }

  private static class InfoSingle extends Info {
    @NotNull private final PsiElement myTargetElement;

    public InfoSingle(@NotNull PsiElement elementAtPointer, @NotNull PsiElement targetElement) {
      super(elementAtPointer);
      myTargetElement = targetElement;
    }

    public InfoSingle(final PsiReference ref, @NotNull final PsiElement targetElement) {
      super(ref.getElement(), ReferenceRange.getAbsoluteRanges(ref));
      myTargetElement = targetElement;
    }

    @Nullable
    public String getInfo() {
      try {
        return generateInfo(myTargetElement, myElementAtPointer);
      } catch (IndexNotReadyException e) {
        showDumbModeNotification(myTargetElement.getProject());
        return null;
      }
    }

    public boolean isValid(Document document) {
      if (!myTargetElement.isValid()) return false;
      if (myTargetElement == myElementAtPointer
          || myTargetElement == myElementAtPointer.getParent()) return false;

      return rangesAreCorrect(document);
    }
  }

  private static class InfoMultiple extends Info {

    public InfoMultiple(@NotNull final PsiElement elementAtPointer) {
      super(elementAtPointer);
    }

    public String getInfo() {
      return CodeInsightBundle.message("multiple.implementations.tooltip");
    }

    public boolean isValid(Document document) {
      return rangesAreCorrect(document);
    }
  }

  @Nullable
  private Info getInfoAt(final Editor editor, PsiFile file, int offset, BrowseMode browseMode) {
    PsiElement targetElement = null;

    if (browseMode == BrowseMode.TypeDeclaration) {
      try {
        targetElement = GotoTypeDeclarationAction.findSymbolType(editor, offset);
      } catch (IndexNotReadyException e) {
        showDumbModeNotification(myProject);
      }
    } else if (browseMode == BrowseMode.Declaration) {
      final PsiReference ref = TargetElementUtilBase.findReference(editor, offset);
      final List<PsiElement> resolvedElements =
          ref != null ? resolve(ref) : Collections.<PsiElement>emptyList();
      final PsiElement resolvedElement =
          resolvedElements.size() == 1 ? resolvedElements.get(0) : null;

      final PsiElement[] targetElements =
          GotoDeclarationAction.findTargetElementsNoVS(myProject, editor, offset);
      final PsiElement elementAtPointer = file.findElementAt(offset);

      if (targetElements != null) {
        if (targetElements.length == 0) {
          return null;
        } else if (targetElements.length == 1) {
          if (targetElements[0] != resolvedElement
              && elementAtPointer != null
              && targetElements[0].isPhysical()) {
            return new InfoSingle(elementAtPointer, targetElements[0]);
          }
        } else {
          return elementAtPointer != null ? new InfoMultiple(elementAtPointer) : null;
        }
      }

      if (resolvedElements.size() == 1) {
        return new InfoSingle(ref, resolvedElements.get(0));
      } else if (resolvedElements.size() > 1) {
        return new InfoMultiple(elementAtPointer);
      }
    } else if (browseMode == BrowseMode.Implementation) {
      final PsiElement element =
          TargetElementUtilBase.getInstance()
              .findTargetElement(editor, ImplementationSearcher.getFlags(), offset);
      PsiElement[] targetElements =
          new ImplementationSearcher() {
            @NotNull
            protected PsiElement[] searchDefinitions(final PsiElement element) {
              final List<PsiElement> found = new ArrayList<PsiElement>(2);
              DefinitionsSearch.search(element)
                  .forEach(
                      new Processor<PsiElement>() {
                        public boolean process(final PsiElement psiElement) {
                          found.add(psiElement);
                          return found.size() != 2;
                        }
                      });
              return PsiUtilBase.toPsiElementArray(found);
            }
          }.searchImplementations(editor, element, offset);
      if (targetElements.length > 1) {
        PsiElement elementAtPointer = file.findElementAt(offset);
        if (elementAtPointer != null) {
          return new InfoMultiple(elementAtPointer);
        }
        return null;
      }
      if (targetElements.length == 1) {
        Navigatable descriptor = EditSourceUtil.getDescriptor(targetElements[0]);
        if (descriptor == null || !descriptor.canNavigate()) {
          return null;
        }
        targetElement = targetElements[0];
      }
    }

    if (targetElement != null && targetElement.isPhysical()) {
      PsiElement elementAtPointer = file.findElementAt(offset);
      if (elementAtPointer != null) {
        return new InfoSingle(elementAtPointer, targetElement);
      }
    }

    return null;
  }

  private static List<PsiElement> resolve(final PsiReference ref) {
    // IDEA-56727 try resolve first as in GotoDeclarationAction
    PsiElement resolvedElement = ref.resolve();

    if (resolvedElement == null && ref instanceof PsiPolyVariantReference) {
      List<PsiElement> result = new ArrayList<PsiElement>();
      final ResolveResult[] psiElements = ((PsiPolyVariantReference) ref).multiResolve(false);
      for (ResolveResult resolveResult : psiElements) {
        if (resolveResult.getElement() != null) {
          result.add(resolveResult.getElement());
        }
      }
      return result;
    }
    return resolvedElement == null
        ? Collections.<PsiElement>emptyList()
        : Collections.singletonList(resolvedElement);
  }

  private void disposeHighlighter() {
    if (myHighlighter != null) {
      myHighlighter.uninstall();
      HintManager.getInstance().hideAllHints();
      myHighlighter = null;
    }
  }

  private class TooltipProvider {
    private final Editor myEditor;
    private final LogicalPosition myPosition;
    private BrowseMode myBrowseMode;
    private boolean myDisposed;

    public TooltipProvider(Editor editor, LogicalPosition pos) {
      myEditor = editor;
      myPosition = pos;
    }

    public void dispose() {
      myDisposed = true;
    }

    public BrowseMode getBrowseMode() {
      return myBrowseMode;
    }

    public void execute(BrowseMode browseMode) {
      myBrowseMode = browseMode;

      Document document = myEditor.getDocument();
      final PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(document);
      if (file == null) return;
      PsiDocumentManager.getInstance(myProject).commitAllDocuments();

      if (TargetElementUtilBase.inVirtualSpace(myEditor, myPosition)) {
        return;
      }

      final int offset = myEditor.logicalPositionToOffset(myPosition);

      int selStart = myEditor.getSelectionModel().getSelectionStart();
      int selEnd = myEditor.getSelectionModel().getSelectionEnd();

      if (offset >= selStart && offset < selEnd) return;

      ApplicationManager.getApplication()
          .executeOnPooledThread(
              new Runnable() {
                public void run() {
                  final ProgressIndicator progressIndicator = new ProgressIndicatorBase();
                  final ApplicationAdapter listener =
                      new ApplicationAdapter() {
                        @Override
                        public void beforeWriteActionStart(Object action) {
                          progressIndicator.cancel();
                        }
                      };
                  final Application application = ApplicationManager.getApplication();
                  try {
                    application.addApplicationListener(listener);
                    ProgressManager.getInstance()
                        .runProcess(
                            new Runnable() {
                              @Override
                              public void run() {
                                // This read action can possibe last for a long time, we want it to
                                // stop immediately on the first write access.
                                // For this purpose we launch it under empty progress and invoke
                                // progressIndicator#cancel on write access to avoid possible write
                                // lock delays.
                                application.runReadAction(
                                    new Runnable() {
                                      public void run() {
                                        doExecute(file, offset);
                                      }
                                    });
                              }
                            },
                            progressIndicator);
                  } finally {
                    application.removeApplicationListener(listener);
                  }
                }
              });
    }

    private void doExecute(PsiFile file, int offset) {
      final Info info;
      try {
        info = getInfoAt(myEditor, file, offset, myBrowseMode);
      } catch (IndexNotReadyException e) {
        showDumbModeNotification(myProject);
        return;
      }
      if (info == null) return;

      SwingUtilities.invokeLater(
          new Runnable() {
            public void run() {
              if (myDisposed || myEditor.isDisposed() || !myEditor.getComponent().isShowing())
                return;
              showHint(info);
            }
          });
    }

    private void showHint(Info info) {
      if (myDisposed) return;
      Component internalComponent = myEditor.getContentComponent();
      if (myHighlighter != null) {
        if (!info.isSimilarTo(myHighlighter.getStoredInfo())) {
          disposeHighlighter();
        } else {
          // highlighter already set
          internalComponent.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
          return;
        }
      }

      if (info.isValid(myEditor.getDocument())) {
        myHighlighter = installHighlighterSet(info, myEditor);

        String text = info.getInfo();

        if (text == null) return;

        JComponent label = HintUtil.createInformationLabel(text);
        final LightweightHint hint = new LightweightHint(label);
        final HintManagerImpl hintManager = HintManagerImpl.getInstanceImpl();
        Point p = HintManagerImpl.getHintPosition(hint, myEditor, myPosition, HintManager.ABOVE);
        hintManager.showEditorHint(
            hint,
            myEditor,
            p,
            HintManager.HIDE_BY_ANY_KEY
                | HintManager.HIDE_BY_TEXT_CHANGE
                | HintManager.HIDE_BY_SCROLLING,
            0,
            false,
            HintManagerImpl.createHintHint(myEditor, p, hint, HintManager.ABOVE)
                .setContentActive(false));
      }
    }
  }

  private HighlightersSet installHighlighterSet(Info info, Editor editor) {
    final JComponent internalComponent = editor.getContentComponent();
    internalComponent.addKeyListener(myEditorKeyListener);
    editor.getScrollingModel().addVisibleAreaListener(myVisibleAreaListener);
    final Cursor cursor = internalComponent.getCursor();
    internalComponent.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
    myFileEditorManager.addFileEditorManagerListener(myFileEditorManagerListener);

    List<RangeHighlighter> highlighters = new ArrayList<RangeHighlighter>();
    for (TextRange range : info.getRanges()) {
      final RangeHighlighter highlighter =
          editor
              .getMarkupModel()
              .addRangeHighlighter(
                  range.getStartOffset(),
                  range.getEndOffset(),
                  HighlighterLayer.SELECTION + 1,
                  ourReferenceAttributes,
                  HighlighterTargetArea.EXACT_RANGE);
      highlighters.add(highlighter);
    }

    return new HighlightersSet(highlighters, editor, cursor, info);
  }

  private class HighlightersSet {
    private final List<RangeHighlighter> myHighlighters;
    private final Editor myHighlighterView;
    private final Cursor myStoredCursor;
    private final Info myStoredInfo;

    private HighlightersSet(
        List<RangeHighlighter> highlighters,
        Editor highlighterView,
        Cursor storedCursor,
        Info storedInfo) {
      myHighlighters = highlighters;
      myHighlighterView = highlighterView;
      myStoredCursor = storedCursor;
      myStoredInfo = storedInfo;
    }

    public void uninstall() {
      for (RangeHighlighter highlighter : myHighlighters) {
        highlighter.dispose();
      }

      Component internalComponent = myHighlighterView.getContentComponent();
      internalComponent.setCursor(myStoredCursor);
      internalComponent.removeKeyListener(myEditorKeyListener);
      myHighlighterView.getScrollingModel().removeVisibleAreaListener(myVisibleAreaListener);
      myFileEditorManager.removeFileEditorManagerListener(myFileEditorManagerListener);
    }

    public Info getStoredInfo() {
      return myStoredInfo;
    }
  }
}
public class NeonSyntaxHighlighter extends SyntaxHighlighterBase {

  public static final String UNKNOWN_ID = "Bad character";
  public static final TextAttributesKey UNKNOWN =
      TextAttributesKey.createTextAttributesKey(
          UNKNOWN_ID, HighlighterColors.BAD_CHARACTER.getDefaultAttributes().clone());

  public static final String COMMENT_ID = "Comment";
  public static final TextAttributesKey COMMENT =
      TextAttributesKey.createTextAttributesKey(
          COMMENT_ID, SyntaxHighlighterColors.LINE_COMMENT.getDefaultAttributes().clone());

  public static final String IDENTIFIER_ID = "Identifier";
  public static final TextAttributesKey IDENTIFIER =
      TextAttributesKey.createTextAttributesKey(
          IDENTIFIER_ID, SyntaxHighlighterColors.KEYWORD.getDefaultAttributes().clone());

  public static final String INTERPUNCTION_ID = "Interpunction";
  public static final TextAttributesKey INTERPUNCTION =
      TextAttributesKey.createTextAttributesKey(
          INTERPUNCTION_ID, SyntaxHighlighterColors.DOT.getDefaultAttributes().clone());

  public static final String NUMBER_ID = "Number";
  public static final TextAttributesKey NUMBER =
      TextAttributesKey.createTextAttributesKey(
          NUMBER_ID, SyntaxHighlighterColors.NUMBER.getDefaultAttributes().clone());

  public static final String KEYWORD_ID = "Keyword";
  public static final TextAttributesKey KEYWORD =
      TextAttributesKey.createTextAttributesKey(
          KEYWORD_ID, SyntaxHighlighterColors.KEYWORD.getDefaultAttributes().clone());

  // Groups of IElementType's
  public static final TokenSet sBAD = TokenSet.create(NEON_UNKNOWN);
  public static final TokenSet sCOMMENTS = TokenSet.create(NEON_COMMENT);
  public static final TokenSet sIDENTIFIERS =
      TokenSet.create(NEON_KEY); // , NEON_IDENTIFIER, NEON_LITERAL);
  public static final TokenSet sINTERPUNCTION =
      TokenSet.create(
          NEON_BLOCK_INHERITENCE,
          NEON_LPAREN,
          NEON_RPAREN,
          NEON_LBRACE_CURLY,
          NEON_RBRACE_CURLY,
          NEON_LBRACE_SQUARE,
          NEON_RBRACE_SQUARE,
          NEON_ITEM_DELIMITER,
          NEON_ASSIGNMENT);
  public static final TokenSet sNUMBERS = TokenSet.create(NEON_NUMBER);

  // Static container
  private static final Map<IElementType, TextAttributesKey> ATTRIBUTES =
      new HashMap<IElementType, TextAttributesKey>();

  // Fill in the map
  static {
    fillMap(ATTRIBUTES, sBAD, UNKNOWN);
    fillMap(ATTRIBUTES, sCOMMENTS, COMMENT);
    fillMap(ATTRIBUTES, sIDENTIFIERS, IDENTIFIER);
    fillMap(ATTRIBUTES, sINTERPUNCTION, INTERPUNCTION);
    fillMap(ATTRIBUTES, sNUMBERS, NUMBER);
  }

  @NotNull
  @Override
  public Lexer getHighlightingLexer() {
    return new NeonLexer();
  }

  @NotNull
  @Override
  public TextAttributesKey[] getTokenHighlights(IElementType type) {
    return pack(ATTRIBUTES.get(type));
  }
}
/** Syntax highlighter defines token colors. */
public class PropsSyntaxHighlighter extends SyntaxHighlighterBase {

  private static final Map<IElementType, TextAttributesKey> keys1;
  private static final Map<IElementType, TextAttributesKey> keys2;

  @NotNull
  public Lexer getHighlightingLexer() {
    return new PropsLexer();
  }

  public static final TextAttributesKey PROP_KEY =
      TextAttributesKey.createTextAttributesKey(
          "PROPS.KEY", SyntaxHighlighterColors.KEYWORD.getDefaultAttributes());
  public static final TextAttributesKey PROP_VALUE =
      TextAttributesKey.createTextAttributesKey(
          "PROPS.VALUE", SyntaxHighlighterColors.STRING.getDefaultAttributes());
  public static final TextAttributesKey PROP_COMMENT =
      TextAttributesKey.createTextAttributesKey(
          "PROPS.LINE_COMMENT", SyntaxHighlighterColors.LINE_COMMENT.getDefaultAttributes());
  public static final TextAttributesKey PROP_KEY_VALUE_SEPARATOR =
      TextAttributesKey.createTextAttributesKey(
          "PROPS.KEY_VALUE_SEPARATOR",
          SyntaxHighlighterColors.OPERATION_SIGN.getDefaultAttributes());
  public static final TextAttributesKey PROP_VALID_STRING_ESCAPE =
      TextAttributesKey.createTextAttributesKey(
          "PROPS.VALID_STRING_ESCAPE",
          SyntaxHighlighterColors.VALID_STRING_ESCAPE.getDefaultAttributes());
  public static final TextAttributesKey PROP_INVALID_STRING_ESCAPE =
      TextAttributesKey.createTextAttributesKey(
          "PROPS.INVALID_STRING_ESCAPE",
          SyntaxHighlighterColors.INVALID_STRING_ESCAPE.getDefaultAttributes());
  public static final TextAttributesKey PROP_SECTION =
      TextAttributesKey.createTextAttributesKey(
          "PROPS.SECTION", new TextAttributes(new Color(0x660066), null, null, null, Font.BOLD));
  public static final TextAttributesKey PROP_MACRO =
      TextAttributesKey.createTextAttributesKey(
          "PROPS.MACRO", new TextAttributes(new Color(0x0055BB), null, null, null, Font.BOLD));
  public static final TextAttributesKey PROP_PROFILE =
      TextAttributesKey.createTextAttributesKey(
          "PROPS.PROFILE", new TextAttributes(new Color(0x0000FF), null, null, null, Font.BOLD));

  static {
    keys1 = new HashMap<IElementType, TextAttributesKey>();
    keys2 = new HashMap<IElementType, TextAttributesKey>();

    keys1.put(PropsTokenTypes.TOKEN_VALUE, PROP_VALUE);
    keys1.put(PropsTokenTypes.TOKEN_EOL_COMMENT, PROP_COMMENT);
    keys1.put(PropsTokenTypes.TOKEN_KEY, PROP_KEY);
    keys1.put(PropsTokenTypes.TOKEN_KEY_VALUE_SEPARATOR, PROP_KEY_VALUE_SEPARATOR);
    keys1.put(PropsTokenTypes.TOKEN_SECTION, PROP_SECTION);
    keys1.put(PropsTokenTypes.TOKEN_MACRO, PROP_MACRO);
    keys1.put(PropsTokenTypes.TOKEN_PROFILE, PROP_PROFILE);

    keys1.put(StringEscapesTokenTypes.VALID_STRING_ESCAPE_TOKEN, PROP_VALID_STRING_ESCAPE);

    // in fact all back-slashed escapes are allowed
    keys1.put(StringEscapesTokenTypes.INVALID_CHARACTER_ESCAPE_TOKEN, PROP_INVALID_STRING_ESCAPE);
    keys1.put(StringEscapesTokenTypes.INVALID_UNICODE_ESCAPE_TOKEN, PROP_INVALID_STRING_ESCAPE);
  }

  @NotNull
  public TextAttributesKey[] getTokenHighlights(IElementType tokenType) {
    return pack(keys1.get(tokenType), keys2.get(tokenType));
  }

  public static final Map<TextAttributesKey, Pair<String, HighlightSeverity>> DISPLAY_NAMES =
      new HashMap<TextAttributesKey, Pair<String, HighlightSeverity>>(7);

  static {
    DISPLAY_NAMES.put(PROP_KEY, new Pair<String, HighlightSeverity>("Property key", null));
    DISPLAY_NAMES.put(PROP_VALUE, new Pair<String, HighlightSeverity>("Property value", null));
    DISPLAY_NAMES.put(
        PROP_KEY_VALUE_SEPARATOR, new Pair<String, HighlightSeverity>("Key value separator", null));
    DISPLAY_NAMES.put(PROP_COMMENT, new Pair<String, HighlightSeverity>("Comment", null));
    DISPLAY_NAMES.put(
        PROP_VALID_STRING_ESCAPE, new Pair<String, HighlightSeverity>("String escape", null));
    DISPLAY_NAMES.put(
        PROP_INVALID_STRING_ESCAPE,
        new Pair<String, HighlightSeverity>("String escape (invalid)", HighlightSeverity.WARNING));
    DISPLAY_NAMES.put(PROP_SECTION, new Pair<String, HighlightSeverity>("Section", null));
    DISPLAY_NAMES.put(PROP_MACRO, new Pair<String, HighlightSeverity>("Macro", null));
    DISPLAY_NAMES.put(PROP_PROFILE, new Pair<String, HighlightSeverity>("Profile", null));
  }
}
 @Override
 public int hashCode() {
   int result = mySeverity.hashCode();
   result = 29 * result + myAttributesKey.hashCode();
   return result;
 }
/** @author Eugene.Kudelevsky */
public class XmlTagTreeHighlightingPass extends TextEditorHighlightingPass {
  private static final Key<List<RangeHighlighter>> TAG_TREE_HIGHLIGHTERS_IN_EDITOR_KEY =
      Key.create("TAG_TREE_HIGHLIGHTERS_IN_EDITOR_KEY");

  private static final HighlightInfoType TYPE =
      new HighlightInfoType.HighlightInfoTypeImpl(
          HighlightSeverity.INFORMATION,
          TextAttributesKey.createTextAttributesKey("TAG_TREE_HIGHLIGHTING_KEY"));

  private final PsiFile myFile;
  private final EditorEx myEditor;
  private final BreadcrumbsInfoProvider myInfoProvider;

  private final List<Pair<TextRange, TextRange>> myPairsToHighlight =
      new ArrayList<Pair<TextRange, TextRange>>();

  public XmlTagTreeHighlightingPass(@NotNull PsiFile file, @NotNull EditorEx editor) {
    super(file.getProject(), editor.getDocument(), true);
    myFile = file;
    myEditor = editor;
    final FileViewProvider viewProvider = file.getManager().findViewProvider(file.getVirtualFile());
    myInfoProvider = BreadcrumbsXmlWrapper.findInfoProvider(viewProvider);
  }

  @Override
  public void doCollectInformation(@NotNull ProgressIndicator progress) {
    if (ApplicationManager.getApplication().isUnitTestMode()) {
      return;
    }

    if (!WebEditorOptions.getInstance().isTagTreeHighlightingEnabled()) {
      return;
    }

    final PsiElement[] elements =
        BreadcrumbsXmlWrapper.getLinePsiElements(
            myEditor.getCaretModel().getOffset(),
            myFile.getVirtualFile(),
            myProject,
            myInfoProvider);

    if (elements == null || elements.length == 0) {
      return;
    }

    if (!XmlTagTreeHighlightingUtil.containsTagsWithSameName(elements)) {
      return;
    }

    for (int i = elements.length - 1; i >= 0; i--) {
      if (elements[i] instanceof XmlTag) {
        myPairsToHighlight.add(getTagRanges((XmlTag) elements[i]));
      }
    }
  }

  @Nullable
  private static Pair<TextRange, TextRange> getTagRanges(XmlTag tag) {
    final ASTNode tagNode = tag.getNode();
    return new Pair<TextRange, TextRange>(getStartTagRange(tagNode), getEndTagRange(tagNode));
  }

  @Nullable
  private static TextRange getStartTagRange(ASTNode tagNode) {
    final ASTNode startTagStart = XmlChildRole.START_TAG_START_FINDER.findChild(tagNode);
    if (startTagStart == null) {
      return null;
    }

    ASTNode tagName = startTagStart.getTreeNext();
    if (tagName == null || tagName.getElementType() != XmlTokenType.XML_NAME) {
      return null;
    }

    ASTNode next = tagName.getTreeNext();
    if (next != null && next.getElementType() == XmlTokenType.XML_TAG_END) {
      tagName = next;
    }

    return new TextRange(startTagStart.getStartOffset(), tagName.getTextRange().getEndOffset());
  }

  @Nullable
  private static TextRange getEndTagRange(ASTNode tagNode) {
    final ASTNode endTagStart = XmlChildRole.CLOSING_TAG_START_FINDER.findChild(tagNode);
    if (endTagStart == null) {
      return null;
    }

    ASTNode endTagEnd = endTagStart;
    while (endTagEnd != null && endTagEnd.getElementType() != XmlTokenType.XML_TAG_END) {
      endTagEnd = endTagEnd.getTreeNext();
    }

    if (endTagEnd == null) {
      return null;
    }

    return new TextRange(endTagStart.getStartOffset(), endTagEnd.getTextRange().getEndOffset());
  }

  @Override
  public void doApplyInformationToEditor() {
    final List<HighlightInfo> infos = getHighlights();
    UpdateHighlightersUtil.setHighlightersToEditor(
        myProject, myDocument, 0, myFile.getTextLength(), infos, getColorsScheme(), getId());
  }

  public List<HighlightInfo> getHighlights() {
    clearLineMarkers(myEditor);

    final int count = myPairsToHighlight.size();
    final List<HighlightInfo> highlightInfos = new ArrayList<HighlightInfo>(count * 2);
    final MarkupModel markupModel = myEditor.getMarkupModel();

    final Color[] baseColors = XmlTagTreeHighlightingUtil.getBaseColors();
    final Color[] colorsForEditor = toColorsForEditor(baseColors);
    final Color[] colorsForLineMarkers = toColorsForLineMarkers(baseColors);

    final List<RangeHighlighter> newHighlighters = new ArrayList<RangeHighlighter>();

    assert colorsForEditor.length > 0;

    for (int i = 0; i < count && i < baseColors.length; i++) {
      Pair<TextRange, TextRange> pair = myPairsToHighlight.get(i);

      if (pair == null || pair.first == null && pair.second == null) {
        continue;
      }

      Color color = colorsForEditor[i];

      if (color == null) {
        continue;
      }

      if (pair.first != null && !pair.first.isEmpty()) {
        highlightInfos.add(createHighlightInfo(color, pair.first));
      }

      if (pair.second != null && !pair.second.isEmpty()) {
        highlightInfos.add(createHighlightInfo(color, pair.second));
      }

      final int start =
          pair.first != null ? pair.first.getStartOffset() : pair.second.getStartOffset();
      final int end = pair.second != null ? pair.second.getEndOffset() : pair.first.getEndOffset();

      final Color lineMarkerColor = colorsForLineMarkers[i];
      if (lineMarkerColor != null && start != end) {
        final RangeHighlighter highlighter =
            createHighlighter(markupModel, new TextRange(start, end), lineMarkerColor);
        newHighlighters.add(highlighter);
      }
    }

    myEditor.putUserData(TAG_TREE_HIGHLIGHTERS_IN_EDITOR_KEY, newHighlighters);

    return highlightInfos;
  }

  private static void clearLineMarkers(Editor editor) {
    final List<RangeHighlighter> oldHighlighters =
        editor.getUserData(TAG_TREE_HIGHLIGHTERS_IN_EDITOR_KEY);

    if (oldHighlighters != null) {
      final MarkupModelEx markupModel = (MarkupModelEx) editor.getMarkupModel();

      for (RangeHighlighter highlighter : oldHighlighters) {
        if (markupModel.containsHighlighter(highlighter)) {
          highlighter.dispose();
        }
      }
      editor.putUserData(TAG_TREE_HIGHLIGHTERS_IN_EDITOR_KEY, null);
    }
  }

  @NotNull
  private static HighlightInfo createHighlightInfo(Color color, @NotNull TextRange range) {
    TextAttributes attributes = new TextAttributes(null, color, null, null, Font.PLAIN);
    return HighlightInfo.newHighlightInfo(TYPE)
        .range(range)
        .textAttributes(attributes)
        .severity(HighlightSeverity.INFORMATION)
        .createUnconditionally();
  }

  @NotNull
  private static RangeHighlighter createHighlighter(
      final MarkupModel mm, @NotNull final TextRange range, final Color color) {
    final RangeHighlighter highlighter =
        mm.addRangeHighlighter(
            range.getStartOffset(),
            range.getEndOffset(),
            0,
            null,
            HighlighterTargetArea.LINES_IN_RANGE);

    highlighter.setLineMarkerRenderer(
        new LineMarkerRenderer() {
          @Override
          public void paint(Editor editor, Graphics g, Rectangle r) {
            int height = r.height + editor.getLineHeight();
            g.setColor(color);
            g.fillRect(r.x, r.y, 2, height);
          }
        });
    return highlighter;
  }

  private static Color[] toColorsForLineMarkers(Color[] baseColors) {
    final Color[] colors = new Color[baseColors.length];
    final Color tagBackground = Gray._239;
    final double transparency = 0.4;
    final double factor = 0.8;

    for (int i = 0; i < colors.length; i++) {
      final Color color = baseColors[i];

      if (color == null) {
        colors[i] = null;
        continue;
      }

      int r = (int) (color.getRed() * factor);
      int g = (int) (color.getGreen() * factor);
      int b = (int) (color.getBlue() * factor);

      r = (int) (tagBackground.getRed() * (1 - transparency) + r * transparency);
      g = (int) (tagBackground.getGreen() * (1 - transparency) + g * transparency);
      b = (int) (tagBackground.getBlue() * (1 - transparency) + b * transparency);

      colors[i] = new Color(r, g, b);
    }

    return colors;
  }

  private Color[] toColorsForEditor(Color[] baseColors) {
    final Color tagBackground = myEditor.getBackgroundColor();

    if (tagBackground == null) {
      return baseColors;
    }

    final Color[] resultColors = new Color[baseColors.length];
    // todo: make configurable
    final double transparency =
        WebEditorOptions.getInstance().getTagTreeHighlightingOpacity() * 0.01;

    for (int i = 0; i < resultColors.length; i++) {
      final Color color = baseColors[i];

      final Color color1 =
          color != null
              ? XmlTagTreeHighlightingUtil.makeTransparent(color, tagBackground, transparency)
              : null;
      resultColors[i] = color1;
    }

    return resultColors;
  }

  public static void clearHighlightingAndLineMarkers(
      final Editor editor, @NotNull Project project) {
    final MarkupModel markupModel =
        DocumentMarkupModel.forDocument(editor.getDocument(), project, true);

    for (RangeHighlighter highlighter : markupModel.getAllHighlighters()) {
      Object tooltip = highlighter.getErrorStripeTooltip();

      if (!(tooltip instanceof HighlightInfo)) {
        continue;
      }

      if (((HighlightInfo) tooltip).type == TYPE) {
        highlighter.dispose();
      }
    }

    clearLineMarkers(editor);
  }
}
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 static final String RAINBOW_TYPE = "rainbow";
  private static final String RAINBOW_TEMP_PREF = "RAINBOW_TEMP_";

  @SuppressWarnings("deprecation")
  public static final TextAttributesKey RAINBOW_ANCHOR =
      TextAttributesKey.createTextAttributesKey(RAINBOW_TYPE, new TextAttributes());

  @SuppressWarnings("deprecation")
  public static final TextAttributesKey RAINBOW_GRADIENT_DEMO =
      TextAttributesKey.createTextAttributesKey("rainbow_demo", new TextAttributes());

  public static final 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;
  }
}
 public static boolean isRainbowTempKey(TextAttributesKey key) {
   return key.getExternalName().startsWith(RAINBOW_TEMP_PREF);
 }
  private int extractExpectedHighlight(
      final Matcher matcher,
      final String text,
      final Document document,
      final Ref<Integer> textOffset) {
    document.deleteString(textOffset.get(), textOffset.get() + matcher.end() - matcher.start());

    int groupIdx = 1;
    final String marker = matcher.group(groupIdx++);
    String descr = matcher.group(groupIdx++);
    final String typeString = matcher.group(groupIdx++);
    final String foregroundColor = matcher.group(groupIdx++);
    final String backgroundColor = matcher.group(groupIdx++);
    final String effectColor = matcher.group(groupIdx++);
    final String effectType = matcher.group(groupIdx++);
    final String fontType = matcher.group(groupIdx++);
    final String attrKey = matcher.group(groupIdx++);
    final String bundleMessage = matcher.group(groupIdx++);
    final boolean closed = matcher.group(groupIdx) != null;

    if (descr == null) {
      descr = ANY_TEXT; // no descr means any string by default
    } else if (descr.equals("null")) {
      descr = null; // explicit "null" descr
    }
    if (descr != null) {
      descr =
          descr.replaceAll(
              "\\\\\\\\\"", "\""); // replace: \\" to ", doesn't check symbol before sequence \\"
      descr = descr.replaceAll("\\\\\"", "\"");
    }

    HighlightInfoType type = WHATEVER;
    if (typeString != null) {
      try {
        type = getTypeByName(typeString);
      } catch (Exception e) {
        LOG.error(e);
      }
      LOG.assertTrue(type != null, "Wrong highlight type: " + typeString);
    }

    TextAttributes forcedAttributes = null;
    if (foregroundColor != null) {
      //noinspection MagicConstant
      forcedAttributes =
          new TextAttributes(
              Color.decode(foregroundColor),
              Color.decode(backgroundColor),
              Color.decode(effectColor),
              EffectType.valueOf(effectType),
              Integer.parseInt(fontType));
    }

    final int rangeStart = textOffset.get();
    final int toContinueFrom;
    if (closed) {
      toContinueFrom = matcher.end();
    } else {
      int pos = matcher.end();
      final Matcher closingTagMatcher = Pattern.compile("</" + marker + ">").matcher(text);
      while (true) {
        if (!closingTagMatcher.find(pos)) {
          LOG.error("Cannot find closing </" + marker + "> in position " + pos);
        }

        final int nextTagStart = matcher.find(pos) ? matcher.start() : text.length();
        if (closingTagMatcher.start() < nextTagStart) {
          textOffset.set(textOffset.get() + closingTagMatcher.start() - pos);
          document.deleteString(
              textOffset.get(),
              textOffset.get() + closingTagMatcher.end() - closingTagMatcher.start());
          toContinueFrom = closingTagMatcher.end();
          break;
        }

        textOffset.set(textOffset.get() + nextTagStart - pos);
        pos = extractExpectedHighlight(matcher, text, document, textOffset);
      }
    }

    final ExpectedHighlightingSet expectedHighlightingSet = myHighlightingTypes.get(marker);
    if (expectedHighlightingSet.enabled) {
      TextAttributesKey forcedTextAttributesKey =
          attrKey == null ? null : TextAttributesKey.createTextAttributesKey(attrKey);
      HighlightInfo.Builder builder =
          HighlightInfo.newHighlightInfo(type)
              .range(rangeStart, textOffset.get())
              .severity(expectedHighlightingSet.severity);

      if (forcedAttributes != null) builder.textAttributes(forcedAttributes);
      if (forcedTextAttributesKey != null) builder.textAttributes(forcedTextAttributesKey);
      if (bundleMessage != null) {
        final List<String> split = StringUtil.split(bundleMessage, "|");
        final ResourceBundle bundle = ResourceBundle.getBundle(split.get(0));
        descr = CommonBundle.message(bundle, split.get(1), split.stream().skip(2).toArray());
      }
      if (descr != null) {
        builder.description(descr);
        builder.unescapedToolTip(descr);
      }
      if (expectedHighlightingSet.endOfLine) builder.endOfLine();
      HighlightInfo highlightInfo = builder.createUnconditionally();
      expectedHighlightingSet.infos.add(highlightInfo);
    }

    return toContinueFrom;
  }
 public void writeExternal(Element element) throws WriteExternalException {
   mySeverity.writeExternal(element);
   myAttributesKey.writeExternal(element);
 }