From 6aa2c918ea593365ec5fe259f157b1d2cb489c77 Mon Sep 17 00:00:00 2001
From: Johannes Mey <johannes.mey@tu-dresden.de>
Date: Mon, 4 Jan 2021 01:01:48 +0100
Subject: [PATCH] add new annotator for T:T components and a quick fix

---
 .../tooling/RelAstGrammarAnnotator.java       | 49 ++++++-----------
 .../tooling/RelAstGrammarJavaAnnotator.java   | 54 +++++++++++++++++++
 ...rammarRemoveRedundantComponentNameFix.java | 54 +++++++++++++++++++
 ...lAstGrammarTypeReferenceImplExtension.java |  1 +
 src/main/resources/META-INF/plugin.xml        |  4 +-
 5 files changed, 127 insertions(+), 35 deletions(-)
 create mode 100644 src/main/java/org/jastadd/tooling/RelAstGrammarJavaAnnotator.java
 create mode 100644 src/main/java/org/jastadd/tooling/RelAstGrammarRemoveRedundantComponentNameFix.java

diff --git a/src/main/java/org/jastadd/tooling/RelAstGrammarAnnotator.java b/src/main/java/org/jastadd/tooling/RelAstGrammarAnnotator.java
index a103a37..ce19247 100644
--- a/src/main/java/org/jastadd/tooling/RelAstGrammarAnnotator.java
+++ b/src/main/java/org/jastadd/tooling/RelAstGrammarAnnotator.java
@@ -6,50 +6,31 @@ import com.intellij.lang.annotation.AnnotationHolder;
 import com.intellij.lang.annotation.Annotator;
 import com.intellij.lang.annotation.HighlightSeverity;
 import com.intellij.openapi.editor.DefaultLanguageHighlighterColors;
-import com.intellij.psi.PsiClass;
 import com.intellij.psi.PsiElement;
-import com.intellij.psi.javadoc.PsiDocComment;
-import com.intellij.psi.javadoc.PsiDocTag;
+import org.jastadd.tooling.psi.RelAstGrammarComponent;
 import org.jetbrains.annotations.NotNull;
 
-import java.util.Arrays;
-import java.util.Optional;
-import java.util.stream.Collectors;
-
-import static org.jastadd.tooling.RelAstGrammarUtil.asReferenceToTypeDecl;
-
 public class RelAstGrammarAnnotator implements Annotator {
 
   @Override
   public void annotate(@NotNull final PsiElement element, @NotNull AnnotationHolder holder) {
 
-    Optional<PsiClass> classOptional = asReferenceToTypeDecl(element);
-
-    if (classOptional.isEmpty()) {
-      return;
-    }
-
-    PsiDocComment docComment = classOptional.get().getDocComment();
-    assert docComment != null; // asReferenceToTypeDecl ensures this is not null
-
-    PsiDocTag declaredAtTag = docComment.findTagByName("declaredat");
-    if (declaredAtTag == null) {
-      return;
-    }
-    String declaredAt = Arrays.stream(declaredAtTag.getDataElements()).map(PsiElement::getText).collect(Collectors.joining());
-
-    PsiDocTag productionTag = docComment.findTagByName("astdecl");
-    if (productionTag == null) {
-      return;
+    if (element instanceof RelAstGrammarComponent) {
+      RelAstGrammarComponent component = (RelAstGrammarComponent) element;
+      if (component.getTypeReference() != null && component.getDeclaredName() != null) {
+        String name = component.getDeclaredName().getText();
+        if (name != null && !name.equals("") && name.equals(component.getTypeReference().getName())) {
+          holder.newAnnotation(HighlightSeverity.WEAK_WARNING, "Redundant name")
+            .range(component.getDeclaredName().getTextRange())
+            .highlightType(ProblemHighlightType.WEAK_WARNING)
+            .textAttributes(DefaultLanguageHighlighterColors.HIGHLIGHTED_REFERENCE)
+            .tooltip("When the name of a component is the same as its type, it can be omitted.")
+            .withFix(new RelAstGrammarRemoveRedundantComponentNameFix(component))
+            .create();
+        }
+      }
     }
-    String production = Arrays.stream(productionTag.getDataElements()).map(PsiElement::getText).collect(Collectors.joining());
 
-    holder.newAnnotation(HighlightSeverity.INFORMATION, "JastAdd Nonterminal: " + production)
-      .range(element.getTextRange())
-      .highlightType(ProblemHighlightType.INFORMATION)
-      .textAttributes(DefaultLanguageHighlighterColors.HIGHLIGHTED_REFERENCE)
-      .tooltip("<b>JastAdd Nonterminal</b><br/>Production: <i>" + production + "</i><br/><i>Declared at </i>" + declaredAt)
-      .create();
   }
 
 }
diff --git a/src/main/java/org/jastadd/tooling/RelAstGrammarJavaAnnotator.java b/src/main/java/org/jastadd/tooling/RelAstGrammarJavaAnnotator.java
new file mode 100644
index 0000000..5beaeff
--- /dev/null
+++ b/src/main/java/org/jastadd/tooling/RelAstGrammarJavaAnnotator.java
@@ -0,0 +1,54 @@
+package org.jastadd.tooling;
+
+
+import com.intellij.codeInspection.ProblemHighlightType;
+import com.intellij.lang.annotation.AnnotationHolder;
+import com.intellij.lang.annotation.Annotator;
+import com.intellij.lang.annotation.HighlightSeverity;
+import com.intellij.openapi.editor.DefaultLanguageHighlighterColors;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.javadoc.PsiDocComment;
+import com.intellij.psi.javadoc.PsiDocTag;
+import org.jastadd.tooling.psi.RelAstGrammarComponent;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import static org.jastadd.tooling.RelAstGrammarUtil.asReferenceToTypeDecl;
+
+public class RelAstGrammarJavaAnnotator implements Annotator {
+
+  @Override
+  public void annotate(@NotNull final PsiElement element, @NotNull AnnotationHolder holder) {
+
+    Optional<PsiClass> classOptional = asReferenceToTypeDecl(element);
+
+    if (classOptional.isPresent()) {
+      PsiDocComment docComment = classOptional.get().getDocComment();
+      assert docComment != null; // asReferenceToTypeDecl ensures this is not null
+
+      PsiDocTag declaredAtTag = docComment.findTagByName("declaredat");
+      if (declaredAtTag == null) {
+        return;
+      }
+      String declaredAt = Arrays.stream(declaredAtTag.getDataElements()).map(PsiElement::getText).collect(Collectors.joining());
+
+      PsiDocTag productionTag = docComment.findTagByName("astdecl");
+      if (productionTag == null) {
+        return;
+      }
+      String production = Arrays.stream(productionTag.getDataElements()).map(PsiElement::getText).collect(Collectors.joining());
+
+      holder.newAnnotation(HighlightSeverity.INFORMATION, "JastAdd Nonterminal: " + production)
+        .range(element.getTextRange())
+        .highlightType(ProblemHighlightType.INFORMATION)
+        .textAttributes(DefaultLanguageHighlighterColors.HIGHLIGHTED_REFERENCE)
+        .tooltip("<b>JastAdd Nonterminal</b><br/>Production: <i>" + production + "</i><br/><i>Declared at </i>" + declaredAt)
+        .create();
+    }
+  }
+
+}
diff --git a/src/main/java/org/jastadd/tooling/RelAstGrammarRemoveRedundantComponentNameFix.java b/src/main/java/org/jastadd/tooling/RelAstGrammarRemoveRedundantComponentNameFix.java
new file mode 100644
index 0000000..01a095d
--- /dev/null
+++ b/src/main/java/org/jastadd/tooling/RelAstGrammarRemoveRedundantComponentNameFix.java
@@ -0,0 +1,54 @@
+package org.jastadd.tooling;
+
+import com.intellij.codeInsight.intention.impl.BaseIntentionAction;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.util.IncorrectOperationException;
+import org.jastadd.tooling.parser.RelAstGrammarTypes;
+import org.jastadd.tooling.psi.RelAstGrammarComponent;
+import org.jetbrains.annotations.NotNull;
+
+public class RelAstGrammarRemoveRedundantComponentNameFix extends BaseIntentionAction {
+
+  private final RelAstGrammarComponent component;
+
+  public RelAstGrammarRemoveRedundantComponentNameFix(RelAstGrammarComponent component) {
+    this.component = component;
+  }
+
+  @NotNull
+  @Override
+  public String getText() {
+    return "Remove the name of component '" + component.getText() + "'";
+  }
+
+  @NotNull
+  @Override
+  public String getFamilyName() {
+    return "Edit component";
+  }
+
+  @Override
+  public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
+    return true;
+  }
+
+  @Override
+  public void invoke(@NotNull final Project project, final Editor editor, PsiFile file) throws
+    IncorrectOperationException {
+    PsiElement first = component.getDeclaredName();
+    PsiElement last = component.getDeclaredName();
+    while (last != null && last.getNode().getElementType() != RelAstGrammarTypes.COL) {
+      last = last.getNextSibling();
+    }
+    if (last == null) {
+      throw new IncorrectOperationException("Unable to find ':' in component definition.");
+    } else {
+      component.deleteChildRange(first, last);
+    }
+  }
+
+
+}
diff --git a/src/main/java/org/jastadd/tooling/psi/impl/RelAstGrammarTypeReferenceImplExtension.java b/src/main/java/org/jastadd/tooling/psi/impl/RelAstGrammarTypeReferenceImplExtension.java
index 74f719d..3b44b50 100644
--- a/src/main/java/org/jastadd/tooling/psi/impl/RelAstGrammarTypeReferenceImplExtension.java
+++ b/src/main/java/org/jastadd/tooling/psi/impl/RelAstGrammarTypeReferenceImplExtension.java
@@ -19,6 +19,7 @@ public class RelAstGrammarTypeReferenceImplExtension extends RelAstGrammarNamedE
   }
 
   public PsiElement setName(@NotNull String newName) {
+    // FIXME this can break the grammar when the type is used in an unnamed component (and in many other cases probably)
     ASTNode keyNode = getNode().getFirstChildNode();
     if (keyNode != null) {
       RelAstGrammarTypeReference name = RelAstGrammarElementFactory.createTypeReference(getProject(), newName);
diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml
index ce813c6..2263a9a 100644
--- a/src/main/resources/META-INF/plugin.xml
+++ b/src/main/resources/META-INF/plugin.xml
@@ -20,7 +20,9 @@
 
         <colorSettingsPage implementation="org.jastadd.tooling.RelAstGrammarColorSettingsPage"/>
 
-        <annotator language="JAVA" implementationClass="org.jastadd.tooling.RelAstGrammarAnnotator"/>
+        <annotator language="JAVA" implementationClass="org.jastadd.tooling.RelAstGrammarJavaAnnotator"/>
+
+        <annotator language="JastAddGrammar" implementationClass="org.jastadd.tooling.RelAstGrammarAnnotator"/>
 
         <codeInsight.lineMarkerProvider language="JAVA"
                                         implementationClass="org.jastadd.tooling.RelAstGrammarLineMarkerProvider"/>
-- 
GitLab