diff --git a/build.gradle b/build.gradle index 50f4f19c27c036eeaa83553930dd228c9c61326f..92351823fc92e56b7d79732121c8cfd8e1348e9c 100644 --- a/build.gradle +++ b/build.gradle @@ -12,6 +12,17 @@ repositories { mavenCentral() } +runIde { + jvmArgs '--add-exports', 'java.base/jdk.internal.vm=ALL-UNNAMED' +} + +//Kotlin DSL: +// +//tasks.runIde { +// jvmArgs("--add-exports", "java.base/jdk.internal.vm=ALL-UNNAMED") +//} + + sourceSets { main.java.srcDirs += "src/gen/java" } diff --git a/src/main/grammar/RelAstGrammar.bnf b/src/main/grammar/RelAstGrammar.bnf index e522f8e51a82aea6b448cd03e90b5816f4753c9a..9e632fc1ed3b88e584102aad6fa817a3ca8f93cb 100644 --- a/src/main/grammar/RelAstGrammar.bnf +++ b/src/main/grammar/RelAstGrammar.bnf @@ -19,7 +19,12 @@ relAstGrammarFile ::= comment* ((type_decl | relation) comment*)* comment ::= (WHITESPACE | MULTILINECOMMENT | DOCCOMMENT | SINGLELINECOMMENT) -type_decl ::= ABSTRACT? declared_name (COL type_reference)? (ASSIGN (component | nta_component)*)? SCOL {methods=[getName]} +type_decl ::= ABSTRACT? declared_name (COL type_reference)? (ASSIGN (component | nta_component)*)? SCOL +{ + mixin="org.jastadd.tooling.psi.impl.RelAstGrammarNamedElementImpl" + implements="org.jastadd.tooling.psi.RelAstGrammarNamedElement" + methods=[getName setName getNameIdentifier] +} nta_component ::= SLASH component SLASH @@ -40,4 +45,10 @@ navigable_role ::= type_reference DOT declared_name (STAR | QUESTION_MARK)? // for auto-completion, it is helpful if we can distinguish the different IDs declared_name ::= ID type_reference ::= ID +{ + mixin="org.jastadd.tooling.psi.impl.RelAstGrammarNamedElementImpl" + implements="org.jastadd.tooling.psi.RelAstGrammarNamedElement" + methods=[getName setName getNameIdentifier] +} + java_name ::= ID diff --git a/src/main/java/org/jastadd/tooling/RelAstGrammarRefactoringSupportProvider.java b/src/main/java/org/jastadd/tooling/RelAstGrammarRefactoringSupportProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..156cc92c88e46ffacec4ceb8c5a1c253f8de7a9a --- /dev/null +++ b/src/main/java/org/jastadd/tooling/RelAstGrammarRefactoringSupportProvider.java @@ -0,0 +1,18 @@ +package org.jastadd.tooling; + + +import com.intellij.lang.refactoring.RefactoringSupportProvider; +import com.intellij.psi.PsiElement; +import org.jastadd.tooling.psi.RelAstGrammarDeclaredName; +import org.jastadd.tooling.psi.RelAstGrammarTypeReference; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class RelAstGrammarRefactoringSupportProvider extends RefactoringSupportProvider { + + @Override + public boolean isMemberInplaceRenameAvailable(@NotNull PsiElement elementToRename, @Nullable PsiElement context) { + return (elementToRename instanceof RelAstGrammarDeclaredName) || (elementToRename instanceof RelAstGrammarTypeReference); + } + +} diff --git a/src/main/java/org/jastadd/tooling/RelAstGrammarReference.java b/src/main/java/org/jastadd/tooling/RelAstGrammarReference.java new file mode 100644 index 0000000000000000000000000000000000000000..09a4de0533fd15fcad069da798988e38450f1662 --- /dev/null +++ b/src/main/java/org/jastadd/tooling/RelAstGrammarReference.java @@ -0,0 +1,63 @@ +package org.jastadd.tooling; + +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.*; +import org.jastadd.tooling.psi.RelAstGrammarTypeDecl; +import org.jastadd.tooling.psi.impl.RelAstGrammarPsiImplUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public class RelAstGrammarReference extends PsiReferenceBase<PsiElement> implements PsiPolyVariantReference { + + private final String key; + + public RelAstGrammarReference(@NotNull PsiElement element, TextRange textRange) { + super(element, textRange); + key = element.getText().substring(textRange.getStartOffset(), textRange.getEndOffset()); + } + + @NotNull + @Override + public ResolveResult[] multiResolve(boolean incompleteCode) { + Project project = myElement.getProject(); + final List<RelAstGrammarTypeDecl> typeDecls = RelAstGrammarUtil.findTypeDecl(project, key); + List<ResolveResult> results = new ArrayList<>(); + for (RelAstGrammarTypeDecl typeDecl : typeDecls) { + results.add(new PsiElementResolveResult(typeDecl)); + } + return results.toArray(new ResolveResult[0]); + } + + @Nullable + @Override + public PsiElement resolve() { + ResolveResult[] resolveResults = multiResolve(false); + return resolveResults.length == 1 ? resolveResults[0].getElement() : null; + } + + @NotNull + @Override + public Object[] getVariants() { + Project project = myElement.getProject(); + List<RelAstGrammarTypeDecl> typeDecls = RelAstGrammarUtil.findTypeDecl(project); + List<LookupElement> variants = new ArrayList<>(); + for (final RelAstGrammarTypeDecl typeDecl : typeDecls) { + if (RelAstGrammarPsiImplUtil.getName(typeDecl) != null && typeDecl.getName().length() > 0) { + variants.add(LookupElementBuilder + .create(typeDecl).withIcon(JastAddIcons.FILE) + .withTypeText(typeDecl.getContainingFile().getName()) + ); + } + } + return variants.toArray(); + } + + +} + diff --git a/src/main/java/org/jastadd/tooling/RelAstGrammarReferenceContributor.java b/src/main/java/org/jastadd/tooling/RelAstGrammarReferenceContributor.java new file mode 100644 index 0000000000000000000000000000000000000000..c302968f251a32de7a41d1f13daee492e243db00 --- /dev/null +++ b/src/main/java/org/jastadd/tooling/RelAstGrammarReferenceContributor.java @@ -0,0 +1,32 @@ +package org.jastadd.tooling; + +import com.intellij.openapi.util.TextRange; +import com.intellij.patterns.PlatformPatterns; +import com.intellij.psi.*; +import com.intellij.util.ProcessingContext; +import org.jastadd.tooling.psi.RelAstGrammarTypeReference; +import org.jetbrains.annotations.NotNull; + +public class RelAstGrammarReferenceContributor extends PsiReferenceContributor { + + @Override + public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) { + registrar.registerReferenceProvider(PlatformPatterns.psiElement(RelAstGrammarTypeReference.class), + new PsiReferenceProvider() { + @NotNull + @Override + public PsiReference[] getReferencesByElement(@NotNull PsiElement element, + @NotNull ProcessingContext context) { + + RelAstGrammarTypeReference typeReference = (RelAstGrammarTypeReference) element; + String value = typeReference.getText(); + if (value != null) { + TextRange range = new TextRange(0, value.length()); + return new PsiReference[]{new RelAstGrammarReference(element, range)}; + } + return PsiReference.EMPTY_ARRAY; + } + }); + } + +} diff --git a/src/main/java/org/jastadd/tooling/psi/RelAstGrammarElementFactory.java b/src/main/java/org/jastadd/tooling/psi/RelAstGrammarElementFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..10a87a86f102f4a236019675f51c03ffa7da9480 --- /dev/null +++ b/src/main/java/org/jastadd/tooling/psi/RelAstGrammarElementFactory.java @@ -0,0 +1,24 @@ +package org.jastadd.tooling.psi; + +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiFileFactory; +import org.jastadd.tooling.RelAstGrammarFileType; +import org.jastadd.tooling.parser.RelAstGrammarTypes; + +public class RelAstGrammarElementFactory { + public static RelAstGrammarDeclaredName createDeclaredName(Project project, String name) { + final RelAstGrammarFile file = createFile(project, name + ";"); + return (RelAstGrammarDeclaredName) file.getFirstChild().getFirstChild(); + } + + public static RelAstGrammarTypeReference createTypeReference(Project project, String name) { + final RelAstGrammarFile file = createFile(project, "X : " + name + ";"); + return (RelAstGrammarTypeReference) (file.getFirstChild().getNode().findChildByType(RelAstGrammarTypes.TYPE_REFERENCE).getPsi()); + } + + public static RelAstGrammarFile createFile(Project project, String text) { + String name = "dummy.relast"; + return (RelAstGrammarFile) PsiFileFactory.getInstance(project). + createFileFromText(name, RelAstGrammarFileType.INSTANCE, text); + } +} diff --git a/src/main/java/org/jastadd/tooling/psi/RelAstGrammarNamedElement.java b/src/main/java/org/jastadd/tooling/psi/RelAstGrammarNamedElement.java new file mode 100644 index 0000000000000000000000000000000000000000..6a7844cf61085aaea84a0da8f70abc27f940b737 --- /dev/null +++ b/src/main/java/org/jastadd/tooling/psi/RelAstGrammarNamedElement.java @@ -0,0 +1,7 @@ +package org.jastadd.tooling.psi; + +import com.intellij.psi.PsiNameIdentifierOwner; + +public interface RelAstGrammarNamedElement extends PsiNameIdentifierOwner { + +} diff --git a/src/main/java/org/jastadd/tooling/psi/RelAstGrammarTypeReferenceManipulator.java b/src/main/java/org/jastadd/tooling/psi/RelAstGrammarTypeReferenceManipulator.java new file mode 100644 index 0000000000000000000000000000000000000000..f05b01aa1bb88a3a7b15d4c9c1d2f4687587974f --- /dev/null +++ b/src/main/java/org/jastadd/tooling/psi/RelAstGrammarTypeReferenceManipulator.java @@ -0,0 +1,41 @@ +package org.jastadd.tooling.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 RelAstGrammarTypeReferenceManipulator extends AbstractElementManipulator<RelAstGrammarTypeReference> implements ElementManipulator<RelAstGrammarTypeReference> { + /** + * 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 RelAstGrammarTypeReference handleContentChange(@NotNull RelAstGrammarTypeReference element, @NotNull TextRange range, String newContent) { + try { + return (RelAstGrammarTypeReference) element.setName(range.replace(element.getText(), newContent)); + } catch (Exception e) { // e.g., in case the range is wrong + throw new IncorrectOperationException(e); + } + } + +// @Nullable +// @Override +// public PsiElement handleContentChange(@NotNull PsiElement element, String newContent) throws IncorrectOperationException { +// if (element instanceof RelAstGrammarTypeReference) { +// RelAstGrammarTypeReference ref = (RelAstGrammarTypeReference) element; +// ref.setName(newContent); +// } else { +// throw new IncorrectOperationException("Called with wrong element type " + element.getClass().getName()); +// } +// return element; +// } +} diff --git a/src/main/java/org/jastadd/tooling/psi/impl/RelAstGrammarNamedElementImpl.java b/src/main/java/org/jastadd/tooling/psi/impl/RelAstGrammarNamedElementImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..fd20d16f96cf2dad7696f9874d49f080413b2734 --- /dev/null +++ b/src/main/java/org/jastadd/tooling/psi/impl/RelAstGrammarNamedElementImpl.java @@ -0,0 +1,28 @@ +package org.jastadd.tooling.psi.impl; + +import com.intellij.extapi.psi.ASTWrapperPsiElement; +import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiReference; +import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry; +import org.jastadd.tooling.psi.RelAstGrammarNamedElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public abstract class RelAstGrammarNamedElementImpl extends ASTWrapperPsiElement implements RelAstGrammarNamedElement { + + public RelAstGrammarNamedElementImpl(@NotNull ASTNode node) { + super(node); + } + + @Nullable + public PsiReference getReference() { + PsiReference[] references = getReferences(); + return references.length == 0 ? null : references[0]; + } + + @NotNull + public PsiReference[] getReferences() { + return ReferenceProvidersRegistry.getReferencesFromProviders(this); + } + +} diff --git a/src/main/java/org/jastadd/tooling/psi/impl/RelAstGrammarPsiImplUtil.java b/src/main/java/org/jastadd/tooling/psi/impl/RelAstGrammarPsiImplUtil.java index e859616ce16fdba33dfd8c2de761094d6b13119e..22766e52a9b2e17d66e52a625cf4babe5efaa6f1 100644 --- a/src/main/java/org/jastadd/tooling/psi/impl/RelAstGrammarPsiImplUtil.java +++ b/src/main/java/org/jastadd/tooling/psi/impl/RelAstGrammarPsiImplUtil.java @@ -1,17 +1,72 @@ package org.jastadd.tooling.psi.impl; import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiElement; import org.jastadd.tooling.parser.RelAstGrammarTypes; +import org.jastadd.tooling.psi.RelAstGrammarDeclaredName; +import org.jastadd.tooling.psi.RelAstGrammarElementFactory; import org.jastadd.tooling.psi.RelAstGrammarTypeDecl; +import org.jastadd.tooling.psi.RelAstGrammarTypeReference; public class RelAstGrammarPsiImplUtil { public static String getName(RelAstGrammarTypeDecl element) { // this finds the *first* ID, which is what we want ASTNode keyNode = element.getNode().findChildByType(RelAstGrammarTypes.DECLARED_NAME); if (keyNode != null) { - // IMPORTANT: Convert embedded escaped spaces to simple spaces return keyNode.getText(); } else { + System.out.println("getName NULL"); + return null; + } + } + + public static PsiElement setName(RelAstGrammarTypeDecl element, String newName) { + ASTNode keyNode = element.getNode().findChildByType(RelAstGrammarTypes.DECLARED_NAME); + if (keyNode != null) { + RelAstGrammarDeclaredName name = RelAstGrammarElementFactory.createDeclaredName(element.getProject(), newName); + ASTNode newKeyNode = name.getNode(); + element.getNode().replaceChild(keyNode, newKeyNode); + } + return element; + } + + public static PsiElement getNameIdentifier(RelAstGrammarTypeDecl element) { + ASTNode keyNode = element.getNode().findChildByType(RelAstGrammarTypes.DECLARED_NAME); + if (keyNode != null) { + return keyNode.getPsi(); + } else { + System.out.println("getNameIdentifier NULL"); + return null; + } + } + + public static String getName(RelAstGrammarTypeReference element) { + // this finds the *first* ID, which is what we want + ASTNode keyNode = element.getNode(); + if (keyNode != null) { + return keyNode.getText(); + } else { + System.out.println("RelAstGrammarTypeReference.getName() returned NULL"); + return null; + } + } + + public static PsiElement setName(RelAstGrammarTypeReference element, String newName) { + ASTNode keyNode = element.getNode().getFirstChildNode(); + if (keyNode != null) { + RelAstGrammarTypeReference name = RelAstGrammarElementFactory.createTypeReference(element.getProject(), newName); + ASTNode newKeyNode = name.getNode().getFirstChildNode(); + element.getNode().replaceChild(keyNode, newKeyNode); + } + return element; + } + + public static PsiElement getNameIdentifier(RelAstGrammarTypeReference element) { + ASTNode keyNode = element.getNode(); + if (keyNode != null) { + return keyNode.getPsi(); + } else { + System.out.println("getNameIdentifier2 NULL"); return null; } } diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 2e3ccc1f4125abd56930578cd5dfef1b33b1ba74..14dfe451c56758b960b913fc81512850482aa529 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -28,8 +28,17 @@ <codeInsight.lineMarkerProvider language="JAVA" implementationClass="org.jastadd.tooling.RelAstGrammarLineMarkerProvider"/> - <completion.contributor language="JastAddGrammar" - implementationClass="org.jastadd.tooling.RelAstGrammarCompletionContributor"/> +<!-- <completion.contributor language="JastAddGrammar"--> +<!-- implementationClass="org.jastadd.tooling.RelAstGrammarCompletionContributor"/>--> + + <psi.referenceContributor implementation="org.jastadd.tooling.RelAstGrammarReferenceContributor"/> + + + <lang.refactoringSupport language="JastAddGrammar" + implementationClass="org.jastadd.tooling.RelAstGrammarRefactoringSupportProvider"/> + <lang.elementManipulator /> + + <lang.elementManipulator forClass="org.jastadd.tooling.psi.RelAstGrammarTypeReference" implementationClass="org.jastadd.tooling.psi.RelAstGrammarTypeReferenceManipulator"/> </extensions> <actions>