package org.elixir_lang.annonator;
import com.intellij.lang.ASTNode;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.Annotator;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiRecursiveElementVisitor;
import org.elixir_lang.ElixirSyntaxHighlighter;
import org.elixir_lang.psi.ElixirMapArguments;
import org.elixir_lang.psi.ElixirMapOperation;
import org.elixir_lang.psi.ElixirStructOperation;
import org.jetbrains.annotations.NotNull;
import static org.elixir_lang.ElixirSyntaxHighlighter.BRACES_TOKEN_SET;
/**
* Annotates maps and structs
*/
public class Map implements Annotator, DumbAware {
/*
* Public Instance Methods
*/
/**
* Annotates the specified PSI element.
* It is guaranteed to be executed in non-reentrant fashion.
* I.e there will be no call of this method for this instance before previous call get completed.
* Multiple instances of the annotator might exist simultaneously, though.
*
* @param element to annotate.
* @param holder the container which receives annotations created by the plugin.
*/
@Override
public void annotate(@NotNull final PsiElement element, @NotNull final AnnotationHolder holder) {
element.accept(
new PsiRecursiveElementVisitor() {
/*
* Public Instance Methods
*/
@Override
public void visitElement(PsiElement element) {
if (element instanceof ElixirMapOperation) {
visitMapOperation((ElixirMapOperation) element);
} else if (element instanceof ElixirStructOperation) {
visitStructOperation((ElixirStructOperation) element);
}
}
/*
* Private Instance Methods
*/
private void visitMapOperation(ElixirMapOperation mapOperation) {
highlight(mapOperation.getMapPrefixOperator(), holder, ElixirSyntaxHighlighter.MAP);
highlight(mapOperation.getMapArguments(), holder, ElixirSyntaxHighlighter.MAP);
}
private void visitStructOperation(ElixirStructOperation structOperation) {
highlight(structOperation.getMapPrefixOperator(), holder, ElixirSyntaxHighlighter.STRUCT);
/* DO NOT highlight mapExpression. It will be highlighted as either an alias or module
attribute, which are more specific and useful. */
highlight(structOperation.getMapArguments(), holder, ElixirSyntaxHighlighter.STRUCT);
}
}
);
}
/*
* Private Instance Methods
*/
private void highlight(@NotNull final ElixirMapArguments mapArguments,
@NotNull AnnotationHolder annotationHolder,
@NotNull final TextAttributesKey textAttributesKey) {
ASTNode[] braces = mapArguments.getNode().getChildren(BRACES_TOKEN_SET);
for (ASTNode brace : braces) {
highlight(brace.getTextRange(), annotationHolder, textAttributesKey);
}
}
private void highlight(@NotNull final PsiElement element,
@NotNull AnnotationHolder annotationHolder,
@NotNull final TextAttributesKey textAttributesKey) {
highlight(element.getTextRange(), annotationHolder, textAttributesKey);
}
/**
* Highlights `textRange` with the given `textAttributesKey`.
*
* @param textRange textRange in the document to highlight
* @param annotationHolder the container which receives annotations created by the plugin.
* @param textAttributesKey text attributes to apply to the `node`.
*/
private void highlight(@NotNull final TextRange textRange,
@NotNull AnnotationHolder annotationHolder,
@NotNull final TextAttributesKey textAttributesKey) {
annotationHolder.createInfoAnnotation(textRange, null)
.setEnforcedTextAttributes(TextAttributes.ERASE_MARKER);
annotationHolder.createInfoAnnotation(textRange, null)
.setEnforcedTextAttributes(
EditorColorsManager.getInstance().getGlobalScheme().getAttributes(textAttributesKey)
);
}
}