| package fuchsia.developer.plugin.fidl; |
| |
| 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.colors.TextAttributesKey; |
| import com.intellij.psi.PsiElement; |
| import com.intellij.psi.tree.IElementType; |
| import fuchsia.developer.plugin.fidl.psi.Types; |
| import java.util.logging.Logger; |
| import org.jetbrains.annotations.NotNull; |
| |
| public class ContextAwareHighlighter implements Annotator { |
| |
| private static final TextAttributesKey IDENTIFIER_ATTRIBUTE; |
| |
| static { |
| SyntaxHighlighter highlighter = new SyntaxHighlighter(); |
| TextAttributesKey[] key = highlighter.getTokenHighlights(Types.IDENTIFIER); |
| IDENTIFIER_ATTRIBUTE = key[0]; |
| } |
| |
| /** |
| * @param literalNode An ASTNode of type NUMERIC_LITERAL. |
| * @return null if the value associated with the node isn't supported or is an unsigned power of |
| * two, an error message otherwise. |
| */ |
| private static String unsignedLongPowerOfTwoOrError(ASTNode literalNode) { |
| // Assume decimal |
| String value = literalNode.getText(); |
| int radix = 10; |
| if (literalNode.findChildByType(Types.BINARY_INTEGRAL_LITERAL) != null) { |
| String[] pieces = value.split("0[bB]"); |
| value = pieces[0] + pieces[1]; |
| radix = 2; |
| } else if (literalNode.findChildByType(Types.HEX_INTEGRAL_LITERAL) != null) { |
| String[] pieces = value.split("0[xX]"); |
| value = pieces[0] + pieces[1]; |
| radix = 16; |
| } |
| |
| Long lval = null; |
| try { |
| lval = Long.parseLong(value, radix); |
| } catch (NumberFormatException e) { |
| // Probably not worth throwing anything. |
| Logger logger = Logger.getLogger(ContextAwareHighlighter.class.getName()); |
| logger.warning("Unknown numeric value " + value); |
| return null; |
| } |
| if (lval < 0 || Long.bitCount(lval) != 1) { |
| return "Bit value must be non-negative power of two, is " + Long.toString(lval, radix); |
| } |
| return null; |
| } |
| |
| @Override |
| public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) { |
| PsiElement parent = element.getParent(); |
| if (parent == null || parent.getNode() == null) { |
| return; |
| } |
| IElementType parentType = parent.getNode().getElementType(); |
| if (parentType == Types.IDENTIFIER_TOKEN) { |
| Annotation annotation = holder.createInfoAnnotation(element, null); |
| annotation.setTextAttributes(IDENTIFIER_ATTRIBUTE); |
| } |
| |
| if (element.getNode().getElementType() == Types.XUNION) { |
| holder.createWarningAnnotation(element, |
| "Xunions are transitional, and will be removed in a future language revision"); |
| } |
| |
| if (element.getNode().getElementType() == Types.BITS_OR_ENUM_MEMBER_VALUE) { |
| IElementType grandParentType = parent.getParent().getNode().getElementType(); |
| if (grandParentType == Types.BITS_DECLARATION) { |
| ASTNode literalNode = element.getNode().findChildByType(Types.INTEGRAL_LITERAL); |
| if (literalNode != null) { |
| // is a numeric value |
| String value = unsignedLongPowerOfTwoOrError(literalNode); |
| if (value != null) { |
| holder.createErrorAnnotation(element, value); |
| } |
| } |
| } |
| } |
| } |
| } |