src/org/intellij/grammar/editor/BnfAnnotator.java (159 lines of code) (raw):

/* * Copyright 2011-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. */ package org.intellij.grammar.editor; import com.intellij.lang.annotation.AnnotationHolder; import com.intellij.lang.annotation.Annotator; import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiReference; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.containers.ContainerUtil; import org.intellij.grammar.KnownAttribute; import org.intellij.grammar.generator.ParserGeneratorUtil; import org.intellij.grammar.generator.RuleGraphHelper; import org.intellij.grammar.psi.*; import org.intellij.grammar.psi.impl.BnfReferenceImpl; import org.intellij.grammar.psi.impl.GrammarUtil; import org.jetbrains.annotations.NotNull; import java.util.Objects; /** * @author gregsh */ final class BnfAnnotator implements Annotator, DumbAware { @Override public void annotate(@NotNull PsiElement psiElement, @NotNull AnnotationHolder annotationHolder) { PsiElement parent = psiElement.getParent(); if (parent instanceof BnfRule rule && rule.getId() == psiElement) { addRuleHighlighting(rule, psiElement, annotationHolder); } else if (parent instanceof BnfAttr attr && attr.getId() == psiElement) { annotationHolder.newSilentAnnotation(HighlightSeverity.INFORMATION) .range(psiElement) .textAttributes(BnfSyntaxHighlighter.ATTRIBUTE) .create(); } else if (parent instanceof BnfModifier) { annotationHolder.newSilentAnnotation(HighlightSeverity.INFORMATION) .range(psiElement) .textAttributes(BnfSyntaxHighlighter.KEYWORD) .create(); } else if (parent instanceof BnfListEntry listEntry && listEntry.getId() == psiElement) { boolean hasValue = listEntry.getLiteralExpression() != null; BnfAttr attr = PsiTreeUtil.getParentOfType(listEntry, BnfAttr.class); KnownAttribute<?> attribute = attr != null ? KnownAttribute.getCompatibleAttribute(attr.getName()) : null; if (attribute == KnownAttribute.METHODS && !hasValue) { annotationHolder.newSilentAnnotation(HighlightSeverity.INFORMATION) .range(psiElement) .textAttributes(BnfSyntaxHighlighter.EXTERNAL) .create(); } } else if (psiElement instanceof BnfReferenceOrToken refOrToken) { if (parent instanceof BnfAttr) { String text = refOrToken.getText(); if ("true".equals(text) || "false".equals(text)) { annotationHolder.newSilentAnnotation(HighlightSeverity.INFORMATION) .range(refOrToken) .textAttributes(BnfSyntaxHighlighter.KEYWORD) .create(); return; } } PsiReference reference = refOrToken.getReference(); Object resolve = reference == null ? null : reference.resolve(); if (resolve instanceof BnfRule rule) { addRuleHighlighting(rule, refOrToken, annotationHolder); } else if (resolve instanceof BnfAttr) { annotationHolder.newSilentAnnotation(HighlightSeverity.INFORMATION) .range(refOrToken) .textAttributes(BnfSyntaxHighlighter.ATTRIBUTE) .create(); } else if (GrammarUtil.isExternalReference(refOrToken)) { if (resolve == null && parent instanceof BnfExternalExpression extExpr && extExpr.getArguments().isEmpty() && ParserGeneratorUtil.Rule.isMeta(ParserGeneratorUtil.Rule.of(refOrToken))) { annotationHolder.newSilentAnnotation(HighlightSeverity.INFORMATION) .range(parent) .textAttributes(BnfSyntaxHighlighter.META_PARAM) .create(); } else { annotationHolder.newSilentAnnotation(HighlightSeverity.INFORMATION) .range(refOrToken) .textAttributes(BnfSyntaxHighlighter.EXTERNAL) .create(); } } else if (resolve == null) { var text = refOrToken.getId().getText(); if (RuleGraphHelper.getTokenNameToTextMap((BnfFile)refOrToken.getContainingFile()).containsKey(text)) { annotationHolder.newSilentAnnotation(HighlightSeverity.INFORMATION) .range(refOrToken) .textAttributes(BnfSyntaxHighlighter.EXPLICIT_TOKEN) .create(); } else { annotationHolder.newSilentAnnotation(HighlightSeverity.INFORMATION) .range(refOrToken) .textAttributes(BnfSyntaxHighlighter.IMPLICIT_TOKEN) .create(); } } } else if (psiElement instanceof BnfStringLiteralExpression) { if (parent instanceof BnfAttrPattern || parent instanceof BnfAttr || parent instanceof BnfListEntry) { annotationHolder.newSilentAnnotation(HighlightSeverity.INFORMATION) .range(psiElement) .enforcedTextAttributes(TextAttributes.ERASE_MARKER) .create(); annotationHolder.newSilentAnnotation(HighlightSeverity.INFORMATION) .range(psiElement) .textAttributes(BnfSyntaxHighlighter.PATTERN) .create(); } if (parent instanceof BnfAttr || parent instanceof BnfListEntry) { String attrName = Objects.requireNonNull(PsiTreeUtil.getParentOfType(psiElement, BnfAttr.class)).getName(); KnownAttribute<?> attribute = KnownAttribute.getCompatibleAttribute(attrName); if (attribute != null) { BnfReferenceImpl<?> reference = ContainerUtil.findInstance(psiElement.getReferences(), BnfReferenceImpl.class); PsiElement resolve = reference == null ? null : reference.resolve(); if (resolve instanceof BnfRule) { TextRange range = reference.getRangeInElement().shiftRight(psiElement.getTextRange().getStartOffset()); annotationHolder.newSilentAnnotation(HighlightSeverity.INFORMATION) .range(range) .textAttributes(BnfSyntaxHighlighter.RULE) .create(); } } } else { String text = ParserGeneratorUtil.getLiteralValue((BnfStringLiteralExpression)psiElement); if (!RuleGraphHelper.getTokenTextToNameMap((BnfFile)psiElement.getContainingFile()).containsKey(text)) { String message = "Tokens matched by text are slower than tokens matched by types"; annotationHolder.newSilentAnnotation(HighlightSeverity.INFORMATION) .range(psiElement) .enforcedTextAttributes(TextAttributes.ERASE_MARKER) .create(); annotationHolder.newAnnotation(HighlightSeverity.INFORMATION, message) .range(psiElement) .textAttributes(BnfSyntaxHighlighter.PATTERN) .create(); } } } } private static void addRuleHighlighting(BnfRule rule, PsiElement psiElement, AnnotationHolder annotationHolder) { annotationHolder.newSilentAnnotation(HighlightSeverity.INFORMATION) .range(psiElement) .textAttributes(ParserGeneratorUtil.Rule.isMeta(rule) ? BnfSyntaxHighlighter.META_RULE : BnfSyntaxHighlighter.RULE) .create(); PsiFile file = rule.getContainingFile(); if (StringUtil.isNotEmpty(((BnfFile)file).findAttributeValue(rule, KnownAttribute.RECOVER_WHILE, null))) { annotationHolder.newSilentAnnotation(HighlightSeverity.INFORMATION) .range(psiElement) .textAttributes(BnfSyntaxHighlighter.RECOVER_MARKER) .create(); } } }