diff --git a/build.gradle.kts b/build.gradle.kts index 0bf2dc5fbc76ba94304158c94ccc3b6ba3a4fc84..2139f7552016728fd2aa8f4c379f9ab3bd23510b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -48,12 +48,6 @@ repositories { jcenter() } -idea { - module { - generatedSourceDirs.add(file("src/gen")) - } -} - // Configure gradle-intellij-plugin plugin. // Read more: https://github.com/JetBrains/gradle-intellij-plugin intellij { @@ -73,6 +67,12 @@ sourceSets { } } +idea { + module { + generatedSourceDirs.add(file("src/gen/java")) + } +} + //import org.jetbrains.grammarkit.tasks.* // grammarKit { @@ -101,9 +101,28 @@ tasks { purgeOldFiles = true } + val generateJastAddAspectLexer = task<GenerateLexer>("generateJastAddAspectLexer") { + source = "src/main/grammar/JastAddAspect.flex" + targetDir = "src/gen/java/org/jastadd/tooling/aspect/lexer/" + targetClass = "JastAddAspectLexer" + purgeOldFiles = true + } + + // not fully working because of https://github.com/JetBrains/gradle-grammar-kit-plugin/issues/3 + val generateJastAddAspectParser = task<GenerateParser>("generateJastAddAspectParser") { + source = "src/main/grammar/JastAddAspect.bnf" + targetRoot = "src/gen/java" + pathToParser = "/org/jastadd/tooling/aspect/JastAddAspectParser.java" + pathToPsiRoot = "/org/jastadd/tooling/aspect/psi" + purgeOldFiles = true + } + + compileJava { dependsOn(generateRelAstGrammarLexer) dependsOn(generateRelAstGrammarParser) + dependsOn(generateRelAstGrammarLexer) + dependsOn(generateRelAstGrammarParser) } withType<JavaCompile> { diff --git a/src/main/grammar/JastAddAspect.bnf b/src/main/grammar/JastAddAspect.bnf new file mode 100644 index 0000000000000000000000000000000000000000..01b821327f07c06bee036c3e9539d835021ddfbb --- /dev/null +++ b/src/main/grammar/JastAddAspect.bnf @@ -0,0 +1,27 @@ +{ + parserClass="org.jastadd.tooling.aspect.parser.JastAddAspectParser" + + extends="com.intellij.extapi.psi.ASTWrapperPsiElement" + + psiClassPrefix="JastAddAspect" + psiImplClassSuffix="Impl" + psiPackage="org.jastadd.tooling.aspect.psi" + psiImplPackage="org.jastadd.tooling.aspect.psi.impl" + + elementTypeHolderClass="org.jastadd.tooling.aspect.psi.JastAddAspectTypes" + elementTypeClass="org.jastadd.tooling.aspect.JastAddAspectElementType" + tokenTypeClass="org.jastadd.tooling.aspect.JastAddAspectTokenType" +} + +jastaddAspectFile ::= (attribute_equation|comment)* + +comment ::= (WHITESPACE | MULTILINECOMMENT | DOCCOMMENT | SINGLELINECOMMENT) + +attribute_equation ::= EQ java_block + +java_block ::= JAVABLOCK +{ + implements="org.jastadd.tooling.aspect.psi.JastAddAspectJavaBlockExtension" + extends="org.jastadd.tooling.aspect.psi.impl.JastAddAspectJavaBlockImplExtension" +} + diff --git a/src/main/grammar/JastAddAspect.flex b/src/main/grammar/JastAddAspect.flex new file mode 100644 index 0000000000000000000000000000000000000000..fafc09948777b0855329ca2b4776b0a1a393317f --- /dev/null +++ b/src/main/grammar/JastAddAspect.flex @@ -0,0 +1,44 @@ +package org.jastadd.tooling.aspect.lexer; + +import com.intellij.lexer.FlexLexer; +import com.intellij.psi.tree.IElementType; +import org.jastadd.tooling.aspect.psi.JastAddAspectTypes; +import com.intellij.psi.TokenType; + +%% + +%class JastAddAspectLexer +%implements FlexLexer +%unicode +%function advance +%type IElementType +%eof{ return; +%eof} + +%{ + private int counter = 0; +%} + + +WhiteSpace = [ ] | \t | \f | \n | \r | \r\n +//ID = [a-zA-Z$_][a-zA-Z0-9$_]* +//MultiLineComment = [/][*][^*]+[*]+([^*/][^*]*[*]+)*[/] +//DocComment = [/][*][*][^*]*[*]+([^*/][^*]*[*]+)*[/] +//SingleLineComment = [/][/] [^\n\r]* (\n | \r | \r\n) +NotParenthesis = [^{}]* + +%state JAVA + +%% + +<YYINITIAL> { + {WhiteSpace}+ {} + "EQ" { yybegin(YYINITIAL); return JastAddAspectTypes.EQ; } + "{" { yybegin(JAVA); counter = 1; } +} + +<JAVA> { + {NotParenthesis} {} + "{" { counter++; yybegin(JAVA); } + "}" { counter--; if (counter==0) { yybegin(YYINITIAL); return JastAddAspectTypes.JAVABLOCK; } } +} diff --git a/src/main/java/org/jastadd/tooling/aspect/JastAddAspectElementType.java b/src/main/java/org/jastadd/tooling/aspect/JastAddAspectElementType.java new file mode 100644 index 0000000000000000000000000000000000000000..4c15673a1bc53924ea6d497db6dab32381c17372 --- /dev/null +++ b/src/main/java/org/jastadd/tooling/aspect/JastAddAspectElementType.java @@ -0,0 +1,13 @@ +package org.jastadd.tooling.aspect; + +import com.intellij.psi.tree.IElementType; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +public class JastAddAspectElementType extends IElementType { + + public JastAddAspectElementType(@NotNull @NonNls String debugName) { + super(debugName, JastAddAspectLanguage.INSTANCE); + } + +} diff --git a/src/main/java/org/jastadd/tooling/aspect/JastAddAspectFile.java b/src/main/java/org/jastadd/tooling/aspect/JastAddAspectFile.java new file mode 100644 index 0000000000000000000000000000000000000000..1d67846a899164b0ac2cc65ffba532c9785baaa0 --- /dev/null +++ b/src/main/java/org/jastadd/tooling/aspect/JastAddAspectFile.java @@ -0,0 +1,25 @@ +package org.jastadd.tooling.aspect; + +import com.intellij.extapi.psi.PsiFileBase; + import com.intellij.openapi.fileTypes.FileType; + import com.intellij.psi.FileViewProvider; + import org.jetbrains.annotations.NotNull; + +public class JastAddAspectFile extends PsiFileBase { + + public JastAddAspectFile(@NotNull FileViewProvider viewProvider) { + super(viewProvider, JastAddAspectLanguage.INSTANCE); + } + + @NotNull + @Override + public FileType getFileType() { + return JastAddAttributeFileType.INSTANCE; + } + + @Override + public String toString() { + return "JastAdd Attribute Aspect File"; + } + +} diff --git a/src/main/java/org/jastadd/tooling/aspect/JastAddAspectFileType.java b/src/main/java/org/jastadd/tooling/aspect/JastAddAspectFileType.java new file mode 100644 index 0000000000000000000000000000000000000000..ede88303f5adfc9ad4efaf279756af547fb96cc1 --- /dev/null +++ b/src/main/java/org/jastadd/tooling/aspect/JastAddAspectFileType.java @@ -0,0 +1,43 @@ +package org.jastadd.tooling.aspect; + +import com.intellij.openapi.fileTypes.LanguageFileType; +import org.jastadd.tooling.JastAddIcons; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; + +public class JastAddAspectFileType extends LanguageFileType { + + public static final JastAddAspectFileType INSTANCE = new JastAddAspectFileType(); + + private JastAddAspectFileType() { + super(JastAddAspectLanguage.INSTANCE); + } + + @NotNull + @Override + public String getName() { + return "JastAdd Aspect"; + } + + @NotNull + @Override + public String getDescription() { + return "JastAdd file (for inter-type declarations)"; + } + + @NotNull + @Override + public String getDefaultExtension() { + return "jadd"; + } + + + @Nullable + @Override + public Icon getIcon() { + return JastAddIcons.FILE; + } + +} diff --git a/src/main/java/org/jastadd/tooling/aspect/JastAddAspectLanguage.java b/src/main/java/org/jastadd/tooling/aspect/JastAddAspectLanguage.java new file mode 100644 index 0000000000000000000000000000000000000000..400caa1ad201c832052d134a4b027219130a248e --- /dev/null +++ b/src/main/java/org/jastadd/tooling/aspect/JastAddAspectLanguage.java @@ -0,0 +1,13 @@ +package org.jastadd.tooling.aspect; + +import com.intellij.lang.Language; + +public class JastAddAspectLanguage extends Language { + + public static final JastAddAspectLanguage INSTANCE = new JastAddAspectLanguage(); + + private JastAddAspectLanguage() { + super("JastAddAspect"); + } + +} diff --git a/src/main/java/org/jastadd/tooling/aspect/JastAddAspectParserDefinition.java b/src/main/java/org/jastadd/tooling/aspect/JastAddAspectParserDefinition.java new file mode 100644 index 0000000000000000000000000000000000000000..7a749b6acc908ba9305e764fbb83c093fd19e981 --- /dev/null +++ b/src/main/java/org/jastadd/tooling/aspect/JastAddAspectParserDefinition.java @@ -0,0 +1,77 @@ +package org.jastadd.tooling.aspect; + +import com.intellij.lang.ASTNode; +import com.intellij.lang.ParserDefinition; +import com.intellij.lang.PsiParser; +import com.intellij.lexer.Lexer; +import com.intellij.openapi.project.Project; +import com.intellij.psi.FileViewProvider; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.TokenType; +import com.intellij.psi.tree.IFileElementType; +import com.intellij.psi.tree.TokenSet; +import org.jastadd.tooling.aspect.lexer.JastAddAspectLexerAdapter; +import org.jastadd.tooling.aspect.parser.JastAddAspectParser; +import org.jastadd.tooling.aspect.psi.JastAddAspectTypes; +import org.jetbrains.annotations.NotNull; + +public class JastAddAspectParserDefinition implements ParserDefinition { + + public static final TokenSet WHITE_SPACES = TokenSet.create(TokenType.WHITE_SPACE); + public static final TokenSet COMMENTS = TokenSet.create(JastAddAspectTypes.COMMENT); + + public static final IFileElementType FILE = new IFileElementType(JastAddAspectLanguage.INSTANCE); + + @NotNull + @Override + public Lexer createLexer(Project project) { + return new JastAddAspectLexerAdapter(); + } + + @NotNull + @Override + public TokenSet getWhitespaceTokens() { + return WHITE_SPACES; + } + + @NotNull + @Override + public TokenSet getCommentTokens() { + return COMMENTS; + } + + @NotNull + @Override + public TokenSet getStringLiteralElements() { + return TokenSet.EMPTY; + } + + @NotNull + @Override + public PsiParser createParser(final Project project) { + return new JastAddAspectParser(); + } + + @Override + public IFileElementType getFileNodeType() { + return FILE; + } + + @Override + public PsiFile createFile(FileViewProvider viewProvider) { + return new JastAddAspectFile(viewProvider); + } + + @Override + public SpaceRequirements spaceExistenceTypeBetweenTokens(ASTNode left, ASTNode right) { + return SpaceRequirements.MAY; + } + + @NotNull + @Override + public PsiElement createElement(ASTNode node) { + return JastAddAspectTypes.Factory.createElement(node); + } + +} diff --git a/src/main/java/org/jastadd/tooling/aspect/JastAddAspectTokenType.java b/src/main/java/org/jastadd/tooling/aspect/JastAddAspectTokenType.java new file mode 100644 index 0000000000000000000000000000000000000000..54d2601f7a6bb0e21368b9761d7ca3947d353382 --- /dev/null +++ b/src/main/java/org/jastadd/tooling/aspect/JastAddAspectTokenType.java @@ -0,0 +1,18 @@ +package org.jastadd.tooling.aspect; + +import com.intellij.psi.tree.IElementType; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +public class JastAddAspectTokenType extends IElementType { + + public JastAddAspectTokenType(@NotNull @NonNls String debugName) { + super(debugName, JastAddAspectLanguage.INSTANCE); + } + + @Override + public String toString() { + return "JastAddAspectTokenType." + super.toString(); + } + +} diff --git a/src/main/java/org/jastadd/tooling/aspect/JastAddAttributeFileType.java b/src/main/java/org/jastadd/tooling/aspect/JastAddAttributeFileType.java new file mode 100644 index 0000000000000000000000000000000000000000..dd9569ef54a3332cfa9f203ec724b9966c3af964 --- /dev/null +++ b/src/main/java/org/jastadd/tooling/aspect/JastAddAttributeFileType.java @@ -0,0 +1,42 @@ +package org.jastadd.tooling.aspect; + +import com.intellij.openapi.fileTypes.LanguageFileType; +import org.jastadd.tooling.JastAddIcons; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; + +public class JastAddAttributeFileType extends LanguageFileType { + + public static final JastAddAttributeFileType INSTANCE = new JastAddAttributeFileType(); + + private JastAddAttributeFileType() { + super(JastAddAspectLanguage.INSTANCE); + } + + @NotNull + @Override + public String getName() { + return "JastAdd Attribute Aspect"; + } + + @NotNull + @Override + public String getDescription() { + return "JastAdd file (for attributes)"; + } + + @NotNull + @Override + public String getDefaultExtension() { + return "jrag"; + } + + @Nullable + @Override + public Icon getIcon() { + return JastAddIcons.FILE; + } + +} diff --git a/src/main/java/org/jastadd/tooling/aspect/JavaBlockLanguageInjector.java b/src/main/java/org/jastadd/tooling/aspect/JavaBlockLanguageInjector.java new file mode 100644 index 0000000000000000000000000000000000000000..298ea5999a6675a61912208090b47e614daf400b --- /dev/null +++ b/src/main/java/org/jastadd/tooling/aspect/JavaBlockLanguageInjector.java @@ -0,0 +1,25 @@ +package org.jastadd.tooling.aspect; + +import com.intellij.lang.Language; +import com.intellij.lang.java.JavaLanguage; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.InjectedLanguagePlaces; +import com.intellij.psi.LanguageInjector; +import com.intellij.psi.PsiLanguageInjectionHost; +import org.jetbrains.annotations.NotNull; + +public class JavaBlockLanguageInjector implements LanguageInjector { + + /** + * @param host PSI element inside which your language will be injected. + * @param injectionPlacesRegistrar stores places where injection occurs. <br> + * Call its {@link InjectedLanguagePlaces#addPlace(Language, TextRange, String, String)} + * method to register particular injection place. + * For example, to inject your language in string literal inside quotes, you might want to <br> + * {@code injectionPlacesRegistrar.addPlace(myLanguage, new TextRange(1,host.getTextLength()-1))} + */ + @Override + public void getLanguagesToInject(@NotNull PsiLanguageInjectionHost host, @NotNull InjectedLanguagePlaces injectionPlacesRegistrar) { + injectionPlacesRegistrar.addPlace(JavaLanguage.INSTANCE, new TextRange(0, host.getTextLength()), "class X { public void m(){", "}"); + } +} diff --git a/src/main/java/org/jastadd/tooling/aspect/lexer/JastAddAspectLexerAdapter.java b/src/main/java/org/jastadd/tooling/aspect/lexer/JastAddAspectLexerAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..d87f6a89665a4c361ed7143f574f6729132ca700 --- /dev/null +++ b/src/main/java/org/jastadd/tooling/aspect/lexer/JastAddAspectLexerAdapter.java @@ -0,0 +1,11 @@ +package org.jastadd.tooling.aspect.lexer; + +import com.intellij.lexer.FlexAdapter; + +public class JastAddAspectLexerAdapter extends FlexAdapter { + + public JastAddAspectLexerAdapter() { + super(new JastAddAspectLexer(null)); + } + +} diff --git a/src/main/java/org/jastadd/tooling/aspect/psi/JastAddAspectJavaBlockExtension.java b/src/main/java/org/jastadd/tooling/aspect/psi/JastAddAspectJavaBlockExtension.java new file mode 100644 index 0000000000000000000000000000000000000000..75b2f3ee760135993ff2094e6bcc3a473c968e63 --- /dev/null +++ b/src/main/java/org/jastadd/tooling/aspect/psi/JastAddAspectJavaBlockExtension.java @@ -0,0 +1,4 @@ +package org.jastadd.tooling.aspect.psi; + +public interface JastAddAspectJavaBlockExtension extends com.intellij.psi.PsiLanguageInjectionHost { +} diff --git a/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectJavaBlockImplExtension.java b/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectJavaBlockImplExtension.java new file mode 100644 index 0000000000000000000000000000000000000000..f1a19e09f95395b05db82bcc3e33e0db00279cf0 --- /dev/null +++ b/src/main/java/org/jastadd/tooling/aspect/psi/impl/JastAddAspectJavaBlockImplExtension.java @@ -0,0 +1,45 @@ +package org.jastadd.tooling.aspect.psi.impl; + +import com.intellij.extapi.psi.ASTWrapperPsiElement; +import com.intellij.lang.ASTNode; +import com.intellij.psi.LiteralTextEscaper; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiLanguageInjectionHost; +import org.jastadd.tooling.aspect.psi.JastAddAspectJavaBlockExtension; +import org.jetbrains.annotations.NotNull; + +public class JastAddAspectJavaBlockImplExtension extends ASTWrapperPsiElement implements JastAddAspectJavaBlockExtension { + + public JastAddAspectJavaBlockImplExtension(@NotNull ASTNode node) { + super(node); + } + + /** + * @return {@code true} if this instance can accept injections, {@code false} otherwise + */ + @Override + public boolean isValidHost() { + return true; + } + + /** + * Update the host element using the provided text of the injected file. It may be required to escape characters from {@code text} + * in accordance with the host language syntax. The implementation may delegate to {@link ElementManipulators#handleContentChange(PsiElement, String)} + * if {@link ElementManipulator} implementation is registered for this element class. + * + * @param text text of the injected file + * @return the updated instance + */ + @Override + public PsiLanguageInjectionHost updateText(@NotNull String text) { + return this; + } + + /** + * @return {@link LiteralTextEscaper} instance which will be used to convert the content of this host element to the content of injected file + */ + @Override + public @NotNull LiteralTextEscaper<? extends PsiLanguageInjectionHost> createLiteralTextEscaper() { + return LiteralTextEscaper.createSimple(this); + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 2263a9a4ae142caa59dc341419b0e4437a1cffcf..12c50ebef59cedd7b630ef32981b6c08600359a6 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -57,6 +57,17 @@ implementation="org.jastadd.tooling.RelAstGrammarLanguageCodeStyleSettingsProvider"/> <lang.commenter language="JastAddGrammar" implementationClass="org.jastadd.tooling.RelAstGrammarCommenter"/> + + <!-- Attribute/Aspect Language --> + + <fileType name="JastAdd Attribute Aspect" implementationClass="org.jastadd.tooling.aspect.JastAddAttributeFileType" + fieldName="INSTANCE" language="JastAddAspect" extensions="jrag"/> + <fileType name="JastAdd Aspect" implementationClass="org.jastadd.tooling.aspect.JastAddAspectFileType" + fieldName="INSTANCE" language="JastAddAspect" extensions="jadd"/> + + <lang.parserDefinition language="JastAddAspect" + implementationClass="org.jastadd.tooling.aspect.JastAddAspectParserDefinition"/> + <languageInjector implementation="org.jastadd.tooling.aspect.JavaBlockLanguageInjector"/> </extensions> <actions>