From 1137b28b1e47b584fa5f8aab6393d84c68e9a94e Mon Sep 17 00:00:00 2001
From: Johannes Mey <johannes.mey@tu-dresden.de>
Date: Mon, 22 Nov 2021 00:23:38 +0100
Subject: [PATCH] renaming also works in aspect files (outside injected java)

---
 src/main/grammar/Aspect.bnf                   | 38 ++++++++++--------
 .../aspect/AspectReferenceContributor.java    | 32 +++++++++++++++
 .../aspect/psi/AspectElementFactory.java      | 28 +++++++++++++
 .../JastAddAspectAstTypeNameManipulator.java  | 29 ++++++++++++++
 ...JastAddAspectAstTypeNameImplExtension.java | 39 +++++++++++++++++++
 src/main/resources/META-INF/plugin.xml        |  5 +++
 6 files changed, 155 insertions(+), 16 deletions(-)
 create mode 100644 src/main/java/org/jastadd/tooling/aspect/AspectReferenceContributor.java
 create mode 100644 src/main/java/org/jastadd/tooling/aspect/psi/AspectElementFactory.java
 create mode 100644 src/main/java/org/jastadd/tooling/aspect/psi/JastAddAspectAstTypeNameManipulator.java
 create mode 100644 src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectAstTypeNameImplExtension.java

diff --git a/src/main/grammar/Aspect.bnf b/src/main/grammar/Aspect.bnf
index c2ada4a..f112d4c 100644
--- a/src/main/grammar/Aspect.bnf
+++ b/src/main/grammar/Aspect.bnf
@@ -101,50 +101,50 @@ aspect_nested_interface_declaration ::= modifiers interface_declaration
 // TODO check if simplification is okay
 aspect_nested_class_declaration ::= modifiers class_declaration
 
-aspect_method_declaration ::= modifiers type_parameters? aspect_result_type IDENTIFIER DOT method_declarator (THROWS name_list)? (block | SEMICOLON)
+aspect_method_declaration ::= modifiers type_parameters? aspect_result_type ast_type_name DOT method_declarator (THROWS name_list)? (block | SEMICOLON)
 
 aspect_refine_method_declaration ::= REFINE [IDENTIFIER] modifiers type_parameters? aspect_result_type IDENTIFIER DOT method_declarator (THROWS name_list)? (block | SEMICOLON)
 
-aspect_constructor_declaration ::= modifiers IDENTIFIER DOT IDENTIFIER formal_parameters (THROWS name_list)? LBRACE explicit_constructor_invocation? block_statement* RBRACE
+aspect_constructor_declaration ::= modifiers ast_type_name DOT ast_type_name formal_parameters (THROWS name_list)? LBRACE explicit_constructor_invocation? block_statement* RBRACE
 
-aspect_refine_constructor_declaration ::= REFINE IDENTIFIER (PUBLIC | PROTECTED | PRIVATE) IDENTIFIER DOT IDENTIFIER formal_parameters (THROWS name_list)? LBRACE block_statement* RBRACE
+aspect_refine_constructor_declaration ::= REFINE IDENTIFIER (PUBLIC | PROTECTED | PRIVATE) ast_type_name DOT IDENTIFIER formal_parameters (THROWS name_list)? LBRACE block_statement* RBRACE
 
-aspect_field_declaration ::= modifiers aspect_type IDENTIFIER DOT variable_declarator (COMMA variable_declarator)* SEMICOLON
+aspect_field_declaration ::= modifiers aspect_type ast_type_name DOT variable_declarator (COMMA variable_declarator)* SEMICOLON
 
-aspect_syn_attribute_declaration ::= annotation* SYN NTA? LAZY? FINAL? aspect_type IDENTIFIER DOT attribute_name LPAREN (type IDENTIFIER (COMMA type IDENTIFIER)* )? RPAREN (CIRCULAR LBRACKET expression RBRACKET)? ( ASSIGN expression SEMICOLON | block | SEMICOLON )
+aspect_syn_attribute_declaration ::= annotation* SYN NTA? LAZY? FINAL? aspect_type ast_type_name DOT attribute_name LPAREN (type IDENTIFIER (COMMA type IDENTIFIER)* )? RPAREN (CIRCULAR LBRACKET expression RBRACKET)? ( ASSIGN expression SEMICOLON | block | SEMICOLON )
 {
   implements="org.jastadd.tooling.aspect.psi.JastAddAspectAttribute"
   extends="org.jastadd.tooling.aspect.psi.impl.JastAddAspectSynAttributeImpl"
 }
 
-aspect_inh_attribute_declaration ::= annotation* INH NTA? LAZY? FINAL? aspect_type IDENTIFIER DOT attribute_name LPAREN (type IDENTIFIER (COMMA type IDENTIFIER)* )? RPAREN (CIRCULAR LBRACKET expression RBRACKET)? SEMICOLON
+aspect_inh_attribute_declaration ::= annotation* INH NTA? LAZY? FINAL? aspect_type ast_type_name DOT attribute_name LPAREN (type IDENTIFIER (COMMA type IDENTIFIER)* )? RPAREN (CIRCULAR LBRACKET expression RBRACKET)? SEMICOLON
 {
   implements="org.jastadd.tooling.aspect.psi.JastAddAspectAttribute"
   extends="org.jastadd.tooling.aspect.psi.impl.JastAddAspectInhAttributeImpl"
 }
 
 // FIXME parentheses are not required around the WHEN expression?
-aspect_rewrite ::= REWRITE IDENTIFIER (IDENTIFIER DOT IDENTIFIER LPAREN RPAREN)? LBRACE ((WHEN LPAREN expression RPAREN)? TO aspect_type ( expression SEMICOLON | block ))+ RBRACE
+aspect_rewrite ::= REWRITE IDENTIFIER (ast_type_name DOT IDENTIFIER LPAREN RPAREN)? LBRACE ((WHEN LPAREN expression RPAREN)? TO aspect_type ( expression SEMICOLON | block ))+ RBRACE
 
-aspect_syn_equation ::= annotation* EQUATION IDENTIFIER DOT attribute_name LPAREN (type IDENTIFIER (COMMA type IDENTIFIER)*)? RPAREN ( ASSIGN expression SEMICOLON | block )
+aspect_syn_equation ::= annotation* EQUATION ast_type_name DOT attribute_name LPAREN (type IDENTIFIER (COMMA type IDENTIFIER)*)? RPAREN ( ASSIGN expression SEMICOLON | block )
 
-aspect_refine_syn_equation ::= REFINE IDENTIFIER EQUATION IDENTIFIER DOT attribute_name LPAREN (type IDENTIFIER (COMMA type IDENTIFIER)*)? RPAREN (ASSIGN expression SEMICOLON | block)
+aspect_refine_syn_equation ::= REFINE IDENTIFIER EQUATION ast_type_name DOT attribute_name LPAREN (type IDENTIFIER (COMMA type IDENTIFIER)*)? RPAREN (ASSIGN expression SEMICOLON | block)
 
-aspect_inh_equation ::= annotation* EQUATION IDENTIFIER DOT IDENTIFIER LPAREN (INT IDENTIFIER)? RPAREN DOT attribute_name LPAREN (type IDENTIFIER (COMMA type IDENTIFIER)*)? RPAREN (ASSIGN expression SEMICOLON | block)
+aspect_inh_equation ::= annotation* EQUATION ast_type_name DOT IDENTIFIER LPAREN (INT IDENTIFIER)? RPAREN DOT attribute_name LPAREN (type IDENTIFIER (COMMA type IDENTIFIER)*)? RPAREN (ASSIGN expression SEMICOLON | block)
 
-aspect_refine_inh_equation ::= REFINE IDENTIFIER EQUATION IDENTIFIER DOT IDENTIFIER LPAREN (INT IDENTIFIER)? RPAREN DOT attribute_name LPAREN (type IDENTIFIER (COMMA type IDENTIFIER)*)? RPAREN (ASSIGN expression SEMICOLON | block)
+aspect_refine_inh_equation ::= REFINE IDENTIFIER EQUATION ast_type_name DOT IDENTIFIER LPAREN (INT IDENTIFIER)? RPAREN DOT attribute_name LPAREN (type IDENTIFIER (COMMA type IDENTIFIER)*)? RPAREN (ASSIGN expression SEMICOLON | block)
 
-collection_attribute ::= annotation* COLL aspect_type IDENTIFIER DOT attribute_name LPAREN RPAREN CIRCULAR? (LBRACKET expression RBRACKET)? (WITH IDENTIFIER)? (ROOT IDENTIFIER)? SEMICOLON
+collection_attribute ::= annotation* COLL aspect_type ast_type_name DOT attribute_name LPAREN RPAREN CIRCULAR? (LBRACKET expression RBRACKET)? (WITH IDENTIFIER)? (ROOT IDENTIFIER)? SEMICOLON
 {
   implements="org.jastadd.tooling.aspect.psi.JastAddAspectAttribute"
   extends="org.jastadd.tooling.aspect.psi.impl.JastAddAspectCollAttributeImpl"
 }
 
-collection_contribution ::= annotation* IDENTIFIER CONTRIBUTES ( NTA expression TO IDENTIFIER DOT attribute_name LPAREN RPAREN | EACH? expression (WHEN expression)? TO IDENTIFIER DOT attribute_name LPAREN RPAREN (FOR  EACH? expression)? | block TO IDENTIFIER DOT attribute_name LPAREN RPAREN ) SEMICOLON
+collection_contribution ::= annotation* ast_type_name CONTRIBUTES ( NTA expression TO ast_type_name DOT attribute_name LPAREN RPAREN | EACH? expression (WHEN expression)? TO ast_type_name DOT attribute_name LPAREN RPAREN (FOR  EACH? expression)? | block TO ast_type_name DOT attribute_name LPAREN RPAREN ) SEMICOLON
 
-aspect_add_interface ::= IDENTIFIER IMPLEMENTS type_name_list SEMICOLON
+aspect_add_interface ::= ast_type_name IMPLEMENTS type_name_list SEMICOLON
 
-aspect_extend_interface ::= IDENTIFIER EXTENDS type_name_list SEMICOLON
+aspect_extend_interface ::= ast_type_name EXTENDS type_name_list SEMICOLON
 
 class_declaration ::= CLASS IDENTIFIER type_parameters? (EXTENDS class_or_interface_type)? (implements type_name_list)? class_body
 
@@ -208,7 +208,7 @@ aspect_result_type ::= VOID | aspect_type
 
 aspect_reference_type ::= aspect_class_or_interface_type (LBRACKET RBRACKET)*
 
-aspect_class_or_interface_type ::= IDENTIFIER type_arguments? (DOT java_identifier type_arguments? )*
+aspect_class_or_interface_type ::= ast_type_name type_arguments? (DOT java_identifier type_arguments? )*
 
 type ::= reference_type | primitive_type
 
@@ -386,5 +386,11 @@ java_identifier ::= IDENTIFIER | INH | SYN | LAZY | REWRITE | TO | WHEN | ASPECT
 
 attribute_name ::= IDENTIFIER
 
+ast_type_name ::= IDENTIFIER
+{
+  extends="org.jastadd.tooling.aspect.psi.impl.JastAddAspectAstTypeNameImplExtension"
+  implements="org.jastadd.tooling.grammar.psi.GrammarNamedElement"
+}
+
 // unused keywords must still appear somewhere
 unused_keywords ::= GOTO | CONST ELLIPSIS | PACKAGE
diff --git a/src/main/java/org/jastadd/tooling/aspect/AspectReferenceContributor.java b/src/main/java/org/jastadd/tooling/aspect/AspectReferenceContributor.java
new file mode 100644
index 0000000..139081c
--- /dev/null
+++ b/src/main/java/org/jastadd/tooling/aspect/AspectReferenceContributor.java
@@ -0,0 +1,32 @@
+package org.jastadd.tooling.aspect;
+
+import com.intellij.openapi.util.TextRange;
+import com.intellij.patterns.PlatformPatterns;
+import com.intellij.psi.*;
+import com.intellij.util.ProcessingContext;
+import org.jastadd.tooling.aspect.psi.JastAddAspectAstTypeName;
+import org.jastadd.tooling.grammar.GrammarReference;
+import org.jetbrains.annotations.NotNull;
+
+public class AspectReferenceContributor extends PsiReferenceContributor {
+
+  @Override
+  public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) {
+    registrar.registerReferenceProvider(PlatformPatterns.psiElement(JastAddAspectAstTypeName.class),
+      new PsiReferenceProvider() {
+        @NotNull
+        @Override
+        public PsiReference @NotNull [] getReferencesByElement(@NotNull PsiElement element,
+                                                               @NotNull ProcessingContext context) {
+          JastAddAspectAstTypeName typeReference = (JastAddAspectAstTypeName) element;
+          String value = typeReference.getText();
+          if (value != null) {
+            TextRange range = new TextRange(0, value.length());
+            return new PsiReference[]{new GrammarReference(element, range)};
+          }
+          return PsiReference.EMPTY_ARRAY;
+        }
+      });
+  }
+
+}
diff --git a/src/main/java/org/jastadd/tooling/aspect/psi/AspectElementFactory.java b/src/main/java/org/jastadd/tooling/aspect/psi/AspectElementFactory.java
new file mode 100644
index 0000000..aa40835
--- /dev/null
+++ b/src/main/java/org/jastadd/tooling/aspect/psi/AspectElementFactory.java
@@ -0,0 +1,28 @@
+package org.jastadd.tooling.aspect.psi;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFileFactory;
+import org.jastadd.tooling.aspect.AspectFileType;
+
+public class AspectElementFactory {
+
+  private AspectElementFactory() {
+    throw new IllegalStateException("Utility class");
+  }
+
+  public static JastAddAspectAstTypeName createComponentName(Project project, String name) {
+    final AspectFile file = createFile(project, "aspect A{syn int " + name + ".attributeName();}");
+    PsiElement result = file.getFirstChild().findElementAt(17);
+    if (result != null) {
+      return (JastAddAspectAstTypeName) result.getParent();
+    }
+    return null;
+  }
+
+  public static AspectFile createFile(Project project, String text) {
+    String name = "dummy.jrag";
+    return (AspectFile) PsiFileFactory.getInstance(project).
+      createFileFromText(name, AspectFileType.INSTANCE, text);
+  }
+}
diff --git a/src/main/java/org/jastadd/tooling/aspect/psi/JastAddAspectAstTypeNameManipulator.java b/src/main/java/org/jastadd/tooling/aspect/psi/JastAddAspectAstTypeNameManipulator.java
new file mode 100644
index 0000000..894ddf8
--- /dev/null
+++ b/src/main/java/org/jastadd/tooling/aspect/psi/JastAddAspectAstTypeNameManipulator.java
@@ -0,0 +1,29 @@
+package org.jastadd.tooling.aspect.psi;
+
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.AbstractElementManipulator;
+import com.intellij.psi.ElementManipulator;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class JastAddAspectAstTypeNameManipulator extends AbstractElementManipulator<JastAddAspectAstTypeName> implements ElementManipulator<JastAddAspectAstTypeName> {
+  /**
+   * Changes the element's text to the given new text.
+   *
+   * @param element    element to be changed
+   * @param range      range within the element
+   * @param newContent new element text
+   * @return changed element
+   * @throws IncorrectOperationException if something goes wrong
+   */
+  @Nullable
+  @Override
+  public JastAddAspectAstTypeName handleContentChange(@NotNull JastAddAspectAstTypeName element, @NotNull TextRange range, String newContent) {
+    try {
+      return (JastAddAspectAstTypeName) element.setName(range.replace(element.getText(), newContent));
+    } catch (Exception e) { // e.g., in case the range is wrong
+      throw new IncorrectOperationException(e);
+    }
+  }
+}
diff --git a/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectAstTypeNameImplExtension.java b/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectAstTypeNameImplExtension.java
new file mode 100644
index 0000000..f4917a4
--- /dev/null
+++ b/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectAstTypeNameImplExtension.java
@@ -0,0 +1,39 @@
+package org.jastadd.tooling.aspect.psi.impl;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.PsiElement;
+import org.jastadd.tooling.aspect.psi.AspectElementFactory;
+import org.jastadd.tooling.aspect.psi.JastAddAspectAstTypeName;
+import org.jastadd.tooling.grammar.psi.GrammarElementFactory;
+import org.jastadd.tooling.grammar.psi.GrammarNamedElement;
+import org.jastadd.tooling.grammar.psi.GrammarTypeName;
+import org.jastadd.tooling.grammar.psi.impl.GrammarNamedElementImpl;
+import org.jetbrains.annotations.NotNull;
+
+public class JastAddAspectAstTypeNameImplExtension extends GrammarNamedElementImpl implements GrammarNamedElement {
+
+  public JastAddAspectAstTypeNameImplExtension(@NotNull ASTNode node) {
+    super(node);
+  }
+
+  public String getName() {
+    // this finds the *first* ID, which is what we want
+    return getNode().getText();
+  }
+
+  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) {
+      JastAddAspectAstTypeName name = AspectElementFactory.createComponentName(getProject(), newName);
+      ASTNode newKeyNode = name.getNode().getFirstChildNode();
+      getNode().replaceChild(keyNode, newKeyNode);
+    }
+    return this;
+  }
+
+  public PsiElement getNameIdentifier() {
+    return getNode().getPsi();
+  }
+
+}
diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml
index c5bdfc6..b4dc17d 100644
--- a/src/main/resources/META-INF/plugin.xml
+++ b/src/main/resources/META-INF/plugin.xml
@@ -79,6 +79,11 @@
         <annotator language="JAVA" implementationClass="org.jastadd.tooling.java.JavaMethodHighlighter"/>
 
         <colorSettingsPage implementation="org.jastadd.tooling.java.JavaColorSettingsPage"/>
+
+        <psi.referenceContributor implementation="org.jastadd.tooling.aspect.AspectReferenceContributor"/>
+
+        <lang.elementManipulator forClass="org.jastadd.tooling.aspect.psi.JastAddAspectAstTypeName"
+                                 implementationClass="org.jastadd.tooling.aspect.psi.JastAddAspectAstTypeNameManipulator"/>
     </extensions>
 
     <actions>
-- 
GitLab