package org.elixir_lang; import com.intellij.lexer.Lexer; import com.intellij.openapi.editor.DefaultLanguageHighlighterColors; import com.intellij.openapi.editor.HighlighterColors; import com.intellij.openapi.editor.colors.CodeInsightColors; import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.openapi.fileTypes.SyntaxHighlighterBase; import com.intellij.psi.TokenType; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import com.intellij.util.xmlb.annotations.Text; import org.elixir_lang.psi.ElixirTypes; import org.jetbrains.annotations.NotNull; import static com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey; /** * Created by luke.imhoff on 8/2/14. */ public class ElixirSyntaxHighlighter extends SyntaxHighlighterBase { public static final TextAttributesKey ALIAS = createTextAttributesKey( "ELIXIR_ALIAS", DefaultLanguageHighlighterColors.CLASS_NAME ); public static final TextAttributesKey ATOM = createTextAttributesKey( "ELIXIR_ATOM", DefaultLanguageHighlighterColors.INSTANCE_FIELD ); static final TextAttributesKey BAD_CHARACTER = createTextAttributesKey( "ELIXIR_BAD_CHARACTER", HighlighterColors.BAD_CHARACTER ); static final TextAttributesKey BIT = createTextAttributesKey( "ELIXIR_BIT" ); public static final TextAttributesKey BRACES = createTextAttributesKey( "ELIXIR_BRACES", DefaultLanguageHighlighterColors.BRACES ); static final TextAttributesKey BRACKETS = createTextAttributesKey( "ELIXIR_BRACKET", DefaultLanguageHighlighterColors.BRACKETS ); static final TextAttributesKey COMMA = createTextAttributesKey( "ELIXIR_COMMA", DefaultLanguageHighlighterColors.COMMA ); static final TextAttributesKey DOT = createTextAttributesKey( "ELIXIR_DOT", DefaultLanguageHighlighterColors.DOT ); public static final TextAttributesKey CHAR_LIST = createTextAttributesKey( "ELIXIR_CHAR_LIST", DefaultLanguageHighlighterColors.STRING ); public static final TextAttributesKey CHAR_TOKEN_TOKEN = createTextAttributesKey( "ELIXIR_CHAR_TOKEN", DefaultLanguageHighlighterColors.MARKUP_ENTITY ); public static final TextAttributesKey COMMENT = createTextAttributesKey( "ELIXIR_COMMENT", DefaultLanguageHighlighterColors.LINE_COMMENT ); public static final TextAttributesKey DECIMAL = createTextAttributesKey( "ELIXIR_DECIMAL", DefaultLanguageHighlighterColors.NUMBER ); public static final TextAttributesKey DOCUMENTATION_MODULE_ATTRIBUTE = createTextAttributesKey( "ELIXIR_DOCUMENTATION_MODULE_ATTRIBUTE", DefaultLanguageHighlighterColors.DOC_COMMENT_TAG ); public static final TextAttributesKey DOCUMENTATION_TEXT = createTextAttributesKey( "ELIXIR_DOCUMENTATION_TEXT", DefaultLanguageHighlighterColors.DOC_COMMENT_TAG_VALUE ); static final TextAttributesKey EXPRESSION_SUBSTITUTION_MARK = createTextAttributesKey( "ELIXIR_EXPRESSION_SUBSTITUTION_MARK", DefaultLanguageHighlighterColors.BRACES ); public static final TextAttributesKey FUNCTION_CALL = createTextAttributesKey( "ELIXIR_FUNCTION_CALL", DefaultLanguageHighlighterColors.FUNCTION_CALL ); public static final TextAttributesKey FUNCTION_DECLARATION = createTextAttributesKey( "ELIXIR_FUNCTION_DECLARATION", DefaultLanguageHighlighterColors.FUNCTION_DECLARATION ); public static final TextAttributesKey IDENTIFIER = createTextAttributesKey( "ELIXIR_IDENTIFIER", DefaultLanguageHighlighterColors.IDENTIFIER ); public static final TextAttributesKey INVALID_DIGIT = createTextAttributesKey( "ELIXIR_INVALID_DIGIT", HighlighterColors.BAD_CHARACTER ); public static final TextAttributesKey KEYWORD = createTextAttributesKey( "ELIXIR_KEYWORD", DefaultLanguageHighlighterColors.KEYWORD ); public static final TextAttributesKey MACRO_CALL = createTextAttributesKey( "ELIXIR_MACRO_CALL", FUNCTION_CALL ); public static final TextAttributesKey MACRO_DECLARATION = createTextAttributesKey( "ELIXIR_MACRO_DECLARATION", FUNCTION_DECLARATION ); public static final TextAttributesKey MAP = createTextAttributesKey( "ELIXIR_MAP", // DO NOT link to {@link ElixirSyntaxHighlighter.BRACES} since that's for Tuples in standard Elixir DefaultLanguageHighlighterColors.BRACES ); public static final TextAttributesKey MODULE_ATTRIBUTE = createTextAttributesKey( "ELIXIR_MODULE_ATTRIBUTE", DefaultLanguageHighlighterColors.CONSTANT ); public static final TextAttributesKey OBSOLETE_WHOLE_NUMBER_BASE = createTextAttributesKey( "ELIXIR_OBSOLETE_WHOLE_NUMBER_BASE", HighlighterColors.BAD_CHARACTER ); public static final TextAttributesKey OPERATION_SIGN = createTextAttributesKey( "ELIXIR_OPERATION_SIGN", DefaultLanguageHighlighterColors.OPERATION_SIGN ); static final TextAttributesKey PARENTHESES = createTextAttributesKey( "ELIXIR_PARENTHESES", DefaultLanguageHighlighterColors.PARENTHESES ); public static final TextAttributesKey PARAMETER = createTextAttributesKey( "ELIXIR_PARAMETER", DefaultLanguageHighlighterColors.PARAMETER ); public static final TextAttributesKey PREDEFINED_CALL = createTextAttributesKey( "ELIXIR_PREDEFINED", DefaultLanguageHighlighterColors.PREDEFINED_SYMBOL ); public static final TextAttributesKey SEMICOLON = createTextAttributesKey( "ELIXIR_SEMICOLON", DefaultLanguageHighlighterColors.SEMICOLON ); public static final TextAttributesKey SIGIL = createTextAttributesKey( "ELIXIR_SIGIL", // Based on color used for Regular expression's boundaries in Ruby DefaultLanguageHighlighterColors.TEMPLATE_LANGUAGE_COLOR ); public static final TextAttributesKey SPECIFICATION = createTextAttributesKey( "ELIXIR_SPECIFICATION", DefaultLanguageHighlighterColors.FUNCTION_DECLARATION ); public static final TextAttributesKey STRUCT = createTextAttributesKey( "ELIXIR_STRUCT", MAP ); public static final TextAttributesKey CALLBACK = createTextAttributesKey( "ELIXIR_CALLBACK", SPECIFICATION ); public static final TextAttributesKey STRING = createTextAttributesKey( "ELIXIR_STRING", DefaultLanguageHighlighterColors.STRING ); public static final TextAttributesKey TYPE = createTextAttributesKey( "ELIXIR_TYPE", DefaultLanguageHighlighterColors.METADATA ); public static final TextAttributesKey TYPE_PARAMETER = createTextAttributesKey( "ELIXIR_TYPE_PARAMETER", DefaultLanguageHighlighterColors.PARAMETER ); public static final TextAttributesKey VALID_DIGIT = createTextAttributesKey( "ELIXIR_VALID_DIGIT", DefaultLanguageHighlighterColors.NUMBER ); public static final TextAttributesKey VALID_ESCAPE_SEQUENCE = createTextAttributesKey( "ELIXIR_VALID_ESCAPE_SEQUENCE", DefaultLanguageHighlighterColors.VALID_STRING_ESCAPE ); public static final TextAttributesKey VARIABLE = createTextAttributesKey( "ELIXIR_VARIABLE", DefaultLanguageHighlighterColors.LOCAL_VARIABLE ); public static final TextAttributesKey IGNORED_VARIABLE = createTextAttributesKey( "ELIXIR_IGNORED_VARIABLE", VARIABLE ); public static final TextAttributesKey WHOLE_NUMBER_BASE = createTextAttributesKey( "ELIXIR_WHOLE_NUMBER_BASE", DefaultLanguageHighlighterColors.NUMBER ); private static final TextAttributesKey[] ALIAS_KEYS = new TextAttributesKey[]{ALIAS}; private static final TextAttributesKey[] ATOM_KEYS = new TextAttributesKey[]{ATOM}; private static final TextAttributesKey[] ATOM_KEYWORD_KEYS = new TextAttributesKey[]{ATOM, KEYWORD}; private static final TextAttributesKey[] BAD_CHAR_KEYS = new TextAttributesKey[]{BAD_CHARACTER}; private static final TextAttributesKey[] BIT_KEYS = new TextAttributesKey[]{BIT}; private static final TextAttributesKey[] BRACES_KEYS = new TextAttributesKey[]{BRACES}; private static final TextAttributesKey[] BRACKETS_KEYS = new TextAttributesKey[]{BRACKETS}; private static final TextAttributesKey[] CHAR_LIST_KEYS = new TextAttributesKey[]{CHAR_LIST}; private static final TextAttributesKey[] CHAR_TOKEN_KEYS = new TextAttributesKey[]{CHAR_TOKEN_TOKEN}; private static final TextAttributesKey[] COMMA_KEYS = new TextAttributesKey[]{COMMA}; private static final TextAttributesKey[] COMMENT_KEYS = new TextAttributesKey[]{COMMENT}; private static final TextAttributesKey[] DECIMAL_KEYS = new TextAttributesKey[]{DECIMAL}; private static final TextAttributesKey[] DOT_KEYS = new TextAttributesKey[]{DOT}; private static final TextAttributesKey[] EMPTY_KEYS = new TextAttributesKey[0]; private static final TextAttributesKey[] EXPRESSION_SUBSTITUTION_MARK_KEYS = new TextAttributesKey[]{EXPRESSION_SUBSTITUTION_MARK}; private static final TextAttributesKey[] IDENTIFIER_KEYS = new TextAttributesKey[]{IDENTIFIER}; private static final TextAttributesKey[] INVALID_DIGITS_KEYS = new TextAttributesKey[]{INVALID_DIGIT}; private static final TextAttributesKey[] KEYWORD_KEYS = new TextAttributesKey[]{KEYWORD}; private static final TextAttributesKey[] OBSOLETE_WHOLE_NUMBER_BASE_KEYS = new TextAttributesKey[]{OBSOLETE_WHOLE_NUMBER_BASE}; private static final TextAttributesKey[] OPERATION_SIGN_KEYS = new TextAttributesKey[]{OPERATION_SIGN}; private static final TextAttributesKey[] PARENTHESES_KEYS = new TextAttributesKey[]{PARENTHESES}; private static final TextAttributesKey[] SEMICOLON_KEYS = new TextAttributesKey[]{SEMICOLON}; private static final TextAttributesKey[] SIGIL_KEYS = new TextAttributesKey[]{SIGIL}; private static final TextAttributesKey[] STRING_KEYS = new TextAttributesKey[]{STRING}; private static final TextAttributesKey[] VALID_DIGITS_KEYS = new TextAttributesKey[]{VALID_DIGIT}; private static final TextAttributesKey[] WHOLE_NUMBER_BASE_KEYS = new TextAttributesKey[]{WHOLE_NUMBER_BASE}; private static final TokenSet ATOMS = TokenSet.create( ElixirTypes.COLON, ElixirTypes.ATOM_FRAGMENT ); private static final TokenSet ATOM_KEYWORDS = TokenSet.create( ElixirTypes.FALSE, ElixirTypes.NIL, ElixirTypes.TRUE ); private static final TokenSet BIT_TOKEN_SET = TokenSet.create( ElixirTypes.CLOSING_BIT, ElixirTypes.OPENING_BIT ); public static final TokenSet BRACES_TOKEN_SET = TokenSet.create( ElixirTypes.OPENING_CURLY, ElixirTypes.CLOSING_CURLY ); private static final TokenSet BRACKETS_TOKEN_SET = TokenSet.create( ElixirTypes.OPENING_BRACKET, ElixirTypes.CLOSING_BRACKET ); private static final TokenSet CHAR_LISTS = TokenSet.create( ElixirTypes.CHAR_LIST_FRAGMENT, ElixirTypes.CHAR_LIST_HEREDOC_PROMOTER, ElixirTypes.CHAR_LIST_HEREDOC_TERMINATOR, ElixirTypes.CHAR_LIST_PROMOTER, ElixirTypes.CHAR_LIST_SIGIL_HEREDOC_PROMOTER, ElixirTypes.CHAR_LIST_SIGIL_HEREDOC_TERMINATOR, ElixirTypes.CHAR_LIST_SIGIL_PROMOTER, ElixirTypes.CHAR_LIST_SIGIL_TERMINATOR, ElixirTypes.CHAR_LIST_TERMINATOR, ElixirTypes.INTERPOLATING_CHAR_LIST_SIGIL_NAME, ElixirTypes.LITERAL_CHAR_LIST_SIGIL_NAME ); private static final TokenSet DECIMAL_TOKEN_SET = TokenSet.create( ElixirTypes.DECIMAL_MARK, ElixirTypes.DECIMAL_SEPARATOR, ElixirTypes.EXPONENT_MARK ); private static final TokenSet EXPRESSION_SUBSTITUTION_MARKS = TokenSet.create( ElixirTypes.INTERPOLATION_START, ElixirTypes.INTERPOLATION_END ); private static final TokenSet INVALID_DIGITS_TOKEN_SET = TokenSet.create( ElixirTypes.INVALID_BINARY_DIGITS, ElixirTypes.INVALID_DECIMAL_DIGITS, ElixirTypes.INVALID_HEXADECIMAL_DIGITS, ElixirTypes.INVALID_OCTAL_DIGITS, ElixirTypes.INVALID_UNKNOWN_BASE_DIGITS ); private static final TokenSet KEYWORD_TOKEN_SET = TokenSet.create( ElixirTypes.AFTER, ElixirTypes.CATCH, ElixirTypes.DO, ElixirTypes.ELSE, ElixirTypes.END, ElixirTypes.FN, ElixirTypes.RESCUE ); private static final TokenSet OBSOLETE_WHOLE_NUMBER_BASE_TOKEN_SET = TokenSet.create( ElixirTypes.OBSOLETE_BINARY_WHOLE_NUMBER_BASE, ElixirTypes.OBSOLETE_HEXADECIMAL_WHOLE_NUMBER_BASE ); private static final TokenSet OPERATION_SIGNS = TokenSet.create( ElixirTypes.AND_OPERATOR, ElixirTypes.ARROW_OPERATOR, ElixirTypes.ASSOCIATION_OPERATOR, ElixirTypes.AT_OPERATOR, ElixirTypes.CAPTURE_OPERATOR, ElixirTypes.COMPARISON_OPERATOR, ElixirTypes.DUAL_OPERATOR, ElixirTypes.IN_OPERATOR, ElixirTypes.IN_MATCH_OPERATOR, ElixirTypes.MATCH_OPERATOR, ElixirTypes.MULTIPLICATION_OPERATOR, ElixirTypes.OR_OPERATOR, ElixirTypes.PIPE_OPERATOR, ElixirTypes.RELATIONAL_OPERATOR, ElixirTypes.STAB_OPERATOR, ElixirTypes.TWO_OPERATOR, ElixirTypes.TYPE_OPERATOR, ElixirTypes.UNARY_OPERATOR, ElixirTypes.WHEN_OPERATOR ); private static final TokenSet PARENTHESES_TOKEN_SET = TokenSet.create( ElixirTypes.CLOSING_PARENTHESIS, ElixirTypes.OPENING_PARENTHESIS ); // @todo Highlight each type of sigil separately and group char list and string with non-sigil versions private static final TokenSet SIGILS = TokenSet.create( ElixirTypes.INTERPOLATING_REGEX_SIGIL_NAME, ElixirTypes.INTERPOLATING_SIGIL_NAME, ElixirTypes.INTERPOLATING_WORDS_SIGIL_NAME, ElixirTypes.LITERAL_REGEX_SIGIL_NAME, ElixirTypes.LITERAL_SIGIL_NAME, ElixirTypes.LITERAL_WORDS_SIGIL_NAME, ElixirTypes.REGEX_FRAGMENT, ElixirTypes.REGEX_HEREDOC_PROMOTER, ElixirTypes.REGEX_HEREDOC_TERMINATOR, ElixirTypes.REGEX_PROMOTER, ElixirTypes.REGEX_TERMINATOR, ElixirTypes.SIGIL_FRAGMENT, ElixirTypes.SIGIL_HEREDOC_PROMOTER, ElixirTypes.SIGIL_HEREDOC_TERMINATOR, ElixirTypes.SIGIL_MODIFIER, ElixirTypes.SIGIL_PROMOTER, ElixirTypes.SIGIL_TERMINATOR, ElixirTypes.TILDE, ElixirTypes.WORDS_FRAGMENT, ElixirTypes.WORDS_HEREDOC_PROMOTER, ElixirTypes.WORDS_HEREDOC_TERMINATOR, ElixirTypes.WORDS_PROMOTER, ElixirTypes.WORDS_TERMINATOR ); private static final TokenSet STRINGS = TokenSet.create( ElixirTypes.INTERPOLATING_STRING_SIGIL_NAME, ElixirTypes.LITERAL_STRING_SIGIL_NAME, ElixirTypes.STRING_FRAGMENT, ElixirTypes.STRING_HEREDOC_PROMOTER, ElixirTypes.STRING_HEREDOC_TERMINATOR, ElixirTypes.STRING_PROMOTER, ElixirTypes.STRING_SIGIL_HEREDOC_PROMOTER, ElixirTypes.STRING_SIGIL_HEREDOC_TERMINATOR, ElixirTypes.STRING_SIGIL_PROMOTER, ElixirTypes.STRING_SIGIL_TERMINATOR, ElixirTypes.STRING_TERMINATOR ); private static final TokenSet VALID_DIGITS_TOKEN_SET = TokenSet.create( ElixirTypes.VALID_BINARY_DIGITS, ElixirTypes.VALID_DECIMAL_DIGITS, ElixirTypes.VALID_DECIMAL_DIGITS, ElixirTypes.VALID_HEXADECIMAL_DIGITS, ElixirTypes.VALID_OCTAL_DIGITS ); private static final TokenSet WHOLE_NUMBER_BASE_TOKEN_SET = TokenSet.create( ElixirTypes.BASE_WHOLE_NUMBER_PREFIX, ElixirTypes.BINARY_WHOLE_NUMBER_BASE, ElixirTypes.HEXADECIMAL_WHOLE_NUMBER_BASE, ElixirTypes.OCTAL_WHOLE_NUMBER_BASE ); @NotNull @Override public Lexer getHighlightingLexer() { return new ElixirLexer(); } @NotNull @Override public TextAttributesKey[] getTokenHighlights(IElementType tokenType) { if (tokenType.equals(ElixirTypes.ALIAS)) { return ALIAS_KEYS; } else if (ATOM_KEYWORDS.contains(tokenType)) { return ATOM_KEYWORD_KEYS; } else if (ATOMS.contains(tokenType)) { return ATOM_KEYS; } else if (tokenType.equals(TokenType.BAD_CHARACTER)) { return BAD_CHAR_KEYS; } else if (BIT_TOKEN_SET.contains(tokenType)) { return BIT_KEYS; } else if (BRACES_TOKEN_SET.contains(tokenType)) { return BRACES_KEYS; } else if (BRACKETS_TOKEN_SET.contains(tokenType)) { return BRACKETS_KEYS; } else if (CHAR_LISTS.contains(tokenType)) { return CHAR_LIST_KEYS; } else if (tokenType == ElixirTypes.CHAR_TOKENIZER) { return CHAR_TOKEN_KEYS; } else if (tokenType.equals(ElixirTypes.COMMA)) { return COMMA_KEYS; } else if (tokenType.equals(ElixirTypes.COMMENT)) { return COMMENT_KEYS; } else if (DECIMAL_TOKEN_SET.contains(tokenType)) { return DECIMAL_KEYS; } else if (tokenType.equals(ElixirTypes.DOT_OPERATOR)) { return DOT_KEYS; } else if (EXPRESSION_SUBSTITUTION_MARKS.contains(tokenType)) { return EXPRESSION_SUBSTITUTION_MARK_KEYS; } else if (tokenType.equals(ElixirTypes.IDENTIFIER)) { return IDENTIFIER_KEYS; } else if (INVALID_DIGITS_TOKEN_SET.contains(tokenType)) { return INVALID_DIGITS_KEYS; } else if (KEYWORD_TOKEN_SET.contains(tokenType)) { return KEYWORD_KEYS; } else if (OBSOLETE_WHOLE_NUMBER_BASE_TOKEN_SET.contains(tokenType)) { return OBSOLETE_WHOLE_NUMBER_BASE_KEYS; } else if (OPERATION_SIGNS.contains(tokenType)) { return OPERATION_SIGN_KEYS; } else if (PARENTHESES_TOKEN_SET.contains(tokenType)) { return PARENTHESES_KEYS; } else if (tokenType.equals(ElixirTypes.SEMICOLON)) { return SEMICOLON_KEYS; } else if (SIGILS.contains(tokenType)) { return SIGIL_KEYS; } else if (STRINGS.contains(tokenType)) { return STRING_KEYS; } else if (VALID_DIGITS_TOKEN_SET.contains(tokenType)) { return VALID_DIGITS_KEYS; } else if (WHOLE_NUMBER_BASE_TOKEN_SET.contains(tokenType)) { return WHOLE_NUMBER_BASE_KEYS; } else { return EMPTY_KEYS; } } }