/** * Copyright 2010-2017 Evgeny Gryaznov * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. */ package org.textmapper.idea.lang.syntax; import com.intellij.lang.ASTNode; import com.intellij.lang.annotation.Annotation; import com.intellij.lang.annotation.AnnotationHolder; import com.intellij.lang.annotation.Annotator; import com.intellij.openapi.editor.DefaultLanguageHighlighterColors; import com.intellij.openapi.editor.colors.CodeInsightColors; import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiWhiteSpace; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; import org.textmapper.idea.lang.syntax.lexer.TMElementType; import org.textmapper.idea.lang.syntax.lexer.TMTokenTypes; import org.textmapper.idea.lang.syntax.lexer.TmToken; import org.textmapper.idea.lang.syntax.psi.*; import java.util.HashSet; import java.util.Set; /** * Gryaznov Evgeny, 1/30/11 */ public class TMAnnotator implements Annotator { public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) { if (element instanceof TmSymbolReference) { TmSymbolReference ref = (TmSymbolReference) element; PsiElement target = ref.resolve(); if (target instanceof TmLexeme) { Annotation infoAnnotation = holder.createInfoAnnotation(ref, null); infoAnnotation.setTextAttributes(TMSyntaxHighlighter.LEXEM_REFERENCE); } else if (target instanceof TmTemplateParam || target instanceof TmNontermParam) { Annotation infoAnnotation = holder.createInfoAnnotation(ref, null); infoAnnotation.setTextAttributes(TMSyntaxHighlighter.NONTERM_PARAMETER_NAME); } else if (target == null) { Annotation infoAnnotation = holder.createErrorAnnotation(ref, "cannot resolve `" + ref.getReferenceText() + "'"); infoAnnotation.setTextAttributes(CodeInsightColors.WRONG_REFERENCES_ATTRIBUTES); } } if (element instanceof TmStateReference) { TmStateReference ref = (TmStateReference) element; if (ref.resolve() == null && !"initial".equals(ref.getReferenceText())) { Annotation infoAnnotation = holder.createErrorAnnotation(ref, "cannot resolve state `" + ref.getReferenceText() + "'"); infoAnnotation.setTextAttributes(CodeInsightColors.WRONG_REFERENCES_ATTRIBUTES); } else { Annotation infoAnnotation = holder.createInfoAnnotation(ref, null); infoAnnotation.setTextAttributes(TMSyntaxHighlighter.START_CONDITION); } } if (element instanceof TmAnnotation) { for (PsiElement el = element.getFirstChild(); el instanceof TmToken || el instanceof PsiWhiteSpace; el = el.getNextSibling()) { if (el instanceof PsiWhiteSpace) continue; TMElementType type = (TMElementType) ((TmToken) el).getTokenType(); if (!(type == TMTokenTypes.OP_AT || type == TMTokenTypes.ID || isSoft(type.getSymbol()))) break; Annotation infoAnnotation = holder.createInfoAnnotation(el, null); infoAnnotation.setTextAttributes(TMSyntaxHighlighter.ANNOTATION); } } if (element instanceof TmRhsLookahead || element instanceof TmLookaheadPredicate) { for (PsiElement el = element.getFirstChild(); el != null; el = el.getNextSibling()) { if (el instanceof PsiWhiteSpace) continue; if (el instanceof TmToken) { IElementType type = ((TmToken) el).getTokenType(); if (!(type == TMTokenTypes.OP_EMARK || type == TMTokenTypes.OP_LPAREN_QA || type == TMTokenTypes.OP_RPAREN || type == TMTokenTypes.OP_AND)) { continue; } Annotation infoAnnotation = holder.createInfoAnnotation(el, null); infoAnnotation.setTextAttributes(TMSyntaxHighlighter.LOOKAHEAD); } else if (el instanceof TmSymbolReference) { Annotation infoAnnotation = holder.createInfoAnnotation(el, null); infoAnnotation.setTextAttributes(TMSyntaxHighlighter.LOOKAHEAD); } } } if (element instanceof TmDirective || element instanceof TmRhsSuffix || element instanceof TmNontermType || element instanceof TmHeader || element instanceof TmStatesClause || element instanceof TmLexemeAttrs || element instanceof TmLexerDirective || element instanceof TmNontermParam || element instanceof TmTemplateParam) { // TODO do not highlight nonempty as a keyword in: %generate nonempty = set(...); for (TmToken token : PsiTreeUtil.getChildrenOfTypeAsList(element, TmToken.class)) { if (isSoft(((TMElementType) token.getTokenType()).getSymbol())) { Annotation infoAnnotation = holder.createInfoAnnotation((ASTNode) token, null); infoAnnotation.setTextAttributes(DefaultLanguageHighlighterColors.KEYWORD); } } } if (element instanceof TmReportClause || element instanceof TmStateMarker) { TextAttributesKey key = element instanceof TmStateMarker ? TMSyntaxHighlighter.STATE_MARKER : TMSyntaxHighlighter.RULE_METADATA; for (PsiElement el = element.getFirstChild(); el != null; el = el.getNextSibling()) { if (el instanceof PsiWhiteSpace) continue; if (el instanceof TmToken) { IElementType type = ((TmToken) el).getTokenType(); if (type == TMTokenTypes.OP_DIV || type == TMTokenTypes.ID) { Annotation infoAnnotation = holder.createInfoAnnotation(el, null); infoAnnotation.setTextAttributes(key); } } else if (el instanceof TmIdentifier) { Annotation infoAnnotation = holder.createInfoAnnotation(el, null); infoAnnotation.setTextAttributes(key); } } } if (element instanceof TmParameterReference) { TmParameterReference ref = (TmParameterReference) element; PsiElement target = ref.resolve(); if (target instanceof TmTemplateParam || target instanceof TmNontermParam) { Annotation infoAnnotation = holder.createInfoAnnotation(ref, null); infoAnnotation.setTextAttributes(TMSyntaxHighlighter.NONTERM_PARAMETER_NAME); } else if (target == null) { Annotation infoAnnotation = holder.createErrorAnnotation(ref, "cannot resolve parameter `" + ref.getReferenceText() + "'"); infoAnnotation.setTextAttributes(CodeInsightColors.WRONG_REFERENCES_ATTRIBUTES); } } } private static Set<Integer> softKeywords = new HashSet<>(); static { for (IElementType softKeyword : TMTokenTypes.softKeywords.getTypes()) { softKeywords.add(((TMElementType) softKeyword).getSymbol()); } } private static boolean isSoft(int symbol) { return softKeywords.contains(symbol); } }