package com.haskforce.highlighting;
import com.haskforce.psi.*;
import com.haskforce.quickfixes.HaskellModuleFilenameFix;
import com.haskforce.quickfixes.HaskellModuleNameFix;
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.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Annotator that:
*
* 1) brushes up syntax highlighting issues from parsing a broken program.
* 2) Registers quickfixes on broken nodes.
*/
public class HaskellAnnotator implements Annotator {
private static final String MSG = "File and module name differs";
@Override
public void annotate(@NotNull final PsiElement element, @NotNull final AnnotationHolder holder) {
element.accept(new HaskellVisitor() {
@Override
public void visitPpragma(@NotNull HaskellPpragma o) {
super.visitPpragma(o);
setHighlighting(o, holder, HaskellSyntaxHighlighter.PRAGMA);
}
@Override
public void visitQvarid(@NotNull HaskellQvarid o) {
super.visitQvarid(o);
setHighlighting(o, holder, HaskellSyntaxHighlighter.VARID);
}
@Override
public void visitQvarsym(@NotNull HaskellQvarsym o) {
super.visitQvarsym(o);
setHighlighting(o, holder, HaskellSyntaxHighlighter.VARSYM);
}
@Override
public void visitQconsym(@NotNull HaskellQconsym o) {
super.visitQconsym(o);
setHighlighting(o, holder, HaskellSyntaxHighlighter.CONSYM);
}
/**
* Highlight the type signature as such.
*/
@Override
public void visitGendecl(@NotNull HaskellGendecl o) {
super.visitGendecl(o);
HaskellVars vars = o.getVars();
if (vars != null) {
vars.getVaridList().forEach(varid -> setHighlighting(varid, holder, HaskellSyntaxHighlighter.SIGNATURE));
}
}
/**
* Highlight function arguments as such.
*/
@Override
public void visitFunorpatdecl(@NotNull HaskellFunorpatdecl o) {
super.visitFunorpatdecl(o);
o.getVaridList()
.stream()
.skip(1) //Skip first, as it is the name of the function.
.forEach(varid -> setHighlighting(varid, holder, HaskellSyntaxHighlighter.PARAMETER));
}
@Override
public void visitQcon(@NotNull HaskellQcon o) {
super.visitQcon(o);
// Highlight the () unit type as a CONID if it's not in an import, e.g. `import Foo ()`.
if (o.getText().equals("()")) {
final PsiElement prev1 = o.getPrevSibling();
final PsiElement prev2 = prev1 == null ? null : prev1.getPrevSibling();
final boolean inImport = prev1 instanceof HaskellImpdecl || prev2 instanceof HaskellImpdecl;
if (!inImport) {
setHighlighting(o, holder, HaskellSyntaxHighlighter.CONID);
}
}
}
@Override
public void visitAtype(@NotNull HaskellAtype o) {
super.visitAtype(o);
// Highlight the () unit type as a CONID.
if (o.getText().equals("()")) {
setHighlighting(o, holder, HaskellSyntaxHighlighter.CONID);
}
}
@Override
public void visitPstringtoken(@NotNull HaskellPstringtoken o) {
super.visitPstringtoken(o);
setHighlighting(o, holder, HaskellSyntaxHighlighter.STRING);
}
@Override
public void visitShebang(@NotNull HaskellShebang o) {
super.visitShebang(o);
setHighlighting(o, holder, HaskellSyntaxHighlighter.COMMENT);
}
@Override
public void visitModuledecl(@NotNull HaskellModuledecl o) {
super.visitModuledecl(o);
final HaskellQconid qc = o.getQconid();
if (qc == null) {
return;
}
final List<HaskellConid> conidList = qc.getConidList();
final HaskellConid lastConid = conidList.get(conidList.size() - 1);
String moduleName = lastConid.getText();
String fullFileName = o.getContainingFile().getName();
//noinspection ConstantConditions
if (fullFileName == null) {
return;
}
String fileSuffix = fullFileName.substring(fullFileName.lastIndexOf('.'));
String fileName = fullFileName.substring(0, fullFileName.length() - fileSuffix.length());
if (!moduleName.equals(fileName) && !"Main".equals(moduleName)) {
HaskellModuleFilenameFix fixFile = new HaskellModuleFilenameFix(moduleName + fileSuffix);
HaskellModuleNameFix fixName = new HaskellModuleNameFix(lastConid, fileName);
holder.createErrorAnnotation(qc, MSG).registerFix(fixFile);
holder.createErrorAnnotation(qc, MSG).registerFix(fixName);
}
}
});
}
private static void setHighlighting(@NotNull PsiElement element, @NotNull AnnotationHolder holder,
@NotNull TextAttributesKey key) {
holder.createInfoAnnotation(element, null).setEnforcedTextAttributes(
EditorColorsManager.getInstance().getGlobalScheme().getAttributes(key));
}
}