├── .gitignore ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src ├── main │ ├── java │ │ └── org │ │ │ └── jusecase │ │ │ └── jte │ │ │ └── intellij │ │ │ └── language │ │ │ ├── JteFoldingBuilder.java │ │ │ ├── KteFoldingBuilder.java │ │ │ ├── JteIcons.java │ │ │ ├── psi │ │ │ ├── JtePsiIf.java │ │ │ ├── JtePsiFor.java │ │ │ ├── JtePsiBlock.java │ │ │ ├── JtePsiComma.java │ │ │ ├── JtePsiElse.java │ │ │ ├── JtePsiEndIf.java │ │ │ ├── JtePsiParam.java │ │ │ ├── JtePsiComment.java │ │ │ ├── JtePsiContent.java │ │ │ ├── JtePsiElseIf.java │ │ │ ├── JtePsiEndFor.java │ │ │ ├── JtePsiEquals.java │ │ │ ├── JtePsiImport.java │ │ │ ├── JtePsiOutput.java │ │ │ ├── JtePsiTemplate.java │ │ │ ├── JtePsiEndContent.java │ │ │ ├── JtePsiOutputEnd.java │ │ │ ├── JtePsiParamsEnd.java │ │ │ ├── JtePsiStatement.java │ │ │ ├── JtePsiConditionEnd.java │ │ │ ├── JtePsiOutputBegin.java │ │ │ ├── JtePsiParamsBegin.java │ │ │ ├── JtePsiStatementEnd.java │ │ │ ├── JtePsiJavaInjection.java │ │ │ ├── JtePsiNameSeparator.java │ │ │ ├── JtePsiConditionBegin.java │ │ │ ├── JtePsiStatementBegin.java │ │ │ ├── JtePsiElement.java │ │ │ ├── JtePsiFile.java │ │ │ ├── KtePsiFile.java │ │ │ ├── KtePsiUtil.java │ │ │ ├── JtePsiJavaContent.java │ │ │ ├── KtePsiJavaContent.java │ │ │ ├── JtePsiExtraJavaInjection.java │ │ │ ├── KtePsiParamName.java │ │ │ ├── JtePsiParamName.java │ │ │ └── JtePsiUtil.java │ │ │ ├── parsing │ │ │ ├── JteLexer.java │ │ │ ├── KteLexer.java │ │ │ ├── parsers │ │ │ │ ├── ElseTokenParser.java │ │ │ │ ├── EndIfTokenParser.java │ │ │ │ ├── EndForTokenParser.java │ │ │ │ ├── CommaTokenParser.java │ │ │ │ ├── IfTokenParser.java │ │ │ │ ├── ForTokenParser.java │ │ │ │ ├── EndContentTokenParser.java │ │ │ │ ├── ParamTokenParser.java │ │ │ │ ├── ImportTokenParser.java │ │ │ │ ├── TemplateTokenParser.java │ │ │ │ ├── ElseIfTokenParser.java │ │ │ │ ├── CommentTokenParser.java │ │ │ │ ├── TemplateNameTokenParser.java │ │ │ │ ├── RawTokenParser.java │ │ │ │ ├── StatementTokenParser.java │ │ │ │ ├── EqualsTokenParser.java │ │ │ │ ├── ForConditionTokenParser.java │ │ │ │ ├── IfConditionTokenParser.java │ │ │ │ ├── TemplateParamsTokenParser.java │ │ │ │ ├── ElseIfConditionTokenParser.java │ │ │ │ ├── OutputTokenParser.java │ │ │ │ ├── ContentBlockTokenParser.java │ │ │ │ └── AbstractTokenParser.java │ │ │ ├── JteElementType.java │ │ │ ├── KteElementType.java │ │ │ ├── JteParser.java │ │ │ ├── KteParser.java │ │ │ ├── TokenTypes.java │ │ │ ├── JteParserDefinition.java │ │ │ ├── KteParserDefinition.java │ │ │ ├── JteTokenTypes.java │ │ │ └── KteTokenTypes.java │ │ │ ├── format │ │ │ ├── JteCodeStyleSettings.java │ │ │ ├── KteCodeStyleSettings.java │ │ │ ├── JteFormattingModelBuilder.java │ │ │ ├── KteFormattingModelBuilder.java │ │ │ ├── JteLanguageCodeStyleSettingsProvider.java │ │ │ ├── KteLanguageCodeStyleSettingsProvider.java │ │ │ ├── FormattingModelBuilderBase.java │ │ │ ├── KteCodeStyleSettingsProvider.java │ │ │ ├── JteCodeStyleSettingsProvider.java │ │ │ └── JteFormattingBlock.java │ │ │ ├── JteTemplateDataElementType.java │ │ │ ├── JteLanguage.java │ │ │ ├── KteLanguage.java │ │ │ ├── file │ │ │ ├── CreateJteFileAction.java │ │ │ └── CreateKteFileAction.java │ │ │ ├── completion │ │ │ ├── JteCompletionContributor.java │ │ │ ├── KteCompletionContributor.java │ │ │ ├── JteContext.java │ │ │ ├── JteCompletionContributorForJava.java │ │ │ ├── KteCompletionContributorForKotlin.java │ │ │ ├── JteTypedActionHandler.java │ │ │ ├── JteHtmlCompletionConfidence.java │ │ │ ├── AbstractTemplateCompletionProvider.java │ │ │ ├── KteTemplateCompletionProvider.java │ │ │ ├── KteTemplateParamCompletionProvider.java │ │ │ ├── JteTemplateParamCompletionProvider.java │ │ │ └── JteTemplateCompletionProvider.java │ │ │ ├── convert │ │ │ ├── JteConvertNotification.java │ │ │ └── JteConvertFromJspAction.java │ │ │ ├── JteHighlightVisitor.java │ │ │ ├── JteFileViewProviderFactory.java │ │ │ ├── KteFileViewProviderFactory.java │ │ │ ├── JteCommenter.java │ │ │ ├── JteFileType.java │ │ │ ├── KteFileType.java │ │ │ ├── JteHighlighterProvider.java │ │ │ ├── KteHighlighterProvider.java │ │ │ ├── refactoring │ │ │ ├── JteSearchScope.java │ │ │ ├── KteSearchScope.java │ │ │ ├── JteUseScopeEnlarger.java │ │ │ ├── JteFindUsagesProvider.java │ │ │ ├── KteFindUsagesProvider.java │ │ │ └── JteMoveFileHandler.java │ │ │ ├── JteHighlighter.java │ │ │ ├── KteHighlighter.java │ │ │ ├── KteBraceMatcher.java │ │ │ ├── JteBraceMatcher.java │ │ │ ├── FoldingBuilderBase.java │ │ │ ├── TemplateHighlighter.java │ │ │ ├── highlighting │ │ │ └── JsExpressionErrorFilter.java │ │ │ ├── HighlighterMapping.java │ │ │ ├── JteJavaContentManipulator.java │ │ │ ├── KteKotlinContentManipulator.java │ │ │ └── JteFileViewProvider.java │ └── resources │ │ ├── liveTemplates │ │ └── jte.xml │ │ └── META-INF │ │ └── pluginIcon.svg └── test │ └── java │ └── org │ └── jusecase │ └── jte │ └── intellij │ └── language │ ├── parsing │ ├── KteLexerTest.java │ └── LexerTest.java │ └── KteKotlinContentManipulatorTest.java ├── README.md ├── .github └── workflows │ └── gradle.yml └── gradlew.bat /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .idea 3 | .gradle 4 | .intellijPlatform 5 | /gradle.properties 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casid/jte-intellij/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/JteFoldingBuilder.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language; 2 | 3 | public class JteFoldingBuilder extends FoldingBuilderBase { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/KteFoldingBuilder.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language; 2 | 3 | public class KteFoldingBuilder extends FoldingBuilderBase { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/JteIcons.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language; 2 | 3 | import com.intellij.icons.AllIcons; 4 | 5 | import javax.swing.*; 6 | 7 | public class JteIcons { 8 | public static final Icon ICON = AllIcons.FileTypes.Html; 9 | } 10 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiIf.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class JtePsiIf extends JtePsiElement { 7 | public JtePsiIf(@NotNull ASTNode node) { 8 | super(node); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiFor.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class JtePsiFor extends JtePsiElement { 7 | public JtePsiFor(@NotNull ASTNode node) { 8 | super(node); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiBlock.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class JtePsiBlock extends JtePsiElement { 7 | public JtePsiBlock(@NotNull ASTNode node) { 8 | super(node); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiComma.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class JtePsiComma extends JtePsiElement { 7 | public JtePsiComma(@NotNull ASTNode node) { 8 | super(node); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiElse.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class JtePsiElse extends JtePsiElement { 7 | public JtePsiElse(@NotNull ASTNode node) { 8 | super(node); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiEndIf.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class JtePsiEndIf extends JtePsiElement { 7 | public JtePsiEndIf(@NotNull ASTNode node) { 8 | super(node); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiParam.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class JtePsiParam extends JtePsiElement { 7 | public JtePsiParam(@NotNull ASTNode node) { 8 | super(node); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiComment.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class JtePsiComment extends JtePsiElement { 7 | public JtePsiComment(@NotNull ASTNode node) { 8 | super(node); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiContent.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class JtePsiContent extends JtePsiElement { 7 | public JtePsiContent(@NotNull ASTNode node) { 8 | super(node); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiElseIf.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class JtePsiElseIf extends JtePsiElement { 7 | public JtePsiElseIf(@NotNull ASTNode node) { 8 | super(node); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiEndFor.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class JtePsiEndFor extends JtePsiElement { 7 | public JtePsiEndFor(@NotNull ASTNode node) { 8 | super(node); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiEquals.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class JtePsiEquals extends JtePsiElement { 7 | public JtePsiEquals(@NotNull ASTNode node) { 8 | super(node); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiImport.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class JtePsiImport extends JtePsiElement { 7 | public JtePsiImport(@NotNull ASTNode node) { 8 | super(node); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiOutput.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class JtePsiOutput extends JtePsiElement { 7 | public JtePsiOutput(@NotNull ASTNode node) { 8 | super(node); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiTemplate.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class JtePsiTemplate extends JtePsiElement { 7 | public JtePsiTemplate(@NotNull ASTNode node) { 8 | super(node); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/JteLexer.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing; 2 | 3 | public class JteLexer extends Lexer { 4 | public JteLexer() { 5 | super(JteTokenTypes.INSTANCE); 6 | } 7 | 8 | @Override 9 | public boolean isExtraParamInjectionRequired() { 10 | return true; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiEndContent.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class JtePsiEndContent extends JtePsiElement { 7 | public JtePsiEndContent(@NotNull ASTNode node) { 8 | super(node); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiOutputEnd.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class JtePsiOutputEnd extends JtePsiElement { 7 | public JtePsiOutputEnd(@NotNull ASTNode node) { 8 | super(node); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiParamsEnd.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class JtePsiParamsEnd extends JtePsiElement { 7 | public JtePsiParamsEnd(@NotNull ASTNode node) { 8 | super(node); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiStatement.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class JtePsiStatement extends JtePsiElement { 7 | public JtePsiStatement(@NotNull ASTNode node) { 8 | super(node); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jte-intellij 2 | 3 | IntelliJ plugin for jte template files. 4 | 5 | Download the latest version at the JetBrains plugin repository 6 | 7 | jte in IntelliJ 8 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/KteLexer.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing; 2 | 3 | public class KteLexer extends Lexer { 4 | public KteLexer() { 5 | super(KteTokenTypes.INSTANCE); 6 | } 7 | 8 | @Override 9 | public boolean isExtraParamInjectionRequired() { 10 | return false; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiConditionEnd.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class JtePsiConditionEnd extends JtePsiElement { 7 | public JtePsiConditionEnd(@NotNull ASTNode node) { 8 | super(node); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiOutputBegin.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class JtePsiOutputBegin extends JtePsiElement { 7 | public JtePsiOutputBegin(@NotNull ASTNode node) { 8 | super(node); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiParamsBegin.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class JtePsiParamsBegin extends JtePsiElement { 7 | public JtePsiParamsBegin(@NotNull ASTNode node) { 8 | super(node); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiStatementEnd.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class JtePsiStatementEnd extends JtePsiElement { 7 | public JtePsiStatementEnd(@NotNull ASTNode node) { 8 | super(node); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiJavaInjection.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class JtePsiJavaInjection extends JtePsiElement { 7 | public JtePsiJavaInjection(@NotNull ASTNode node) { 8 | super(node); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiNameSeparator.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class JtePsiNameSeparator extends JtePsiElement { 7 | public JtePsiNameSeparator(@NotNull ASTNode node) { 8 | super(node); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiConditionBegin.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class JtePsiConditionBegin extends JtePsiElement { 7 | public JtePsiConditionBegin(@NotNull ASTNode node) { 8 | super(node); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiStatementBegin.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public class JtePsiStatementBegin extends JtePsiElement { 7 | public JtePsiStatementBegin(@NotNull ASTNode node) { 8 | super(node); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/format/JteCodeStyleSettings.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.format; 2 | 3 | import com.intellij.psi.codeStyle.CodeStyleSettings; 4 | import com.intellij.psi.codeStyle.CustomCodeStyleSettings; 5 | 6 | public class JteCodeStyleSettings extends CustomCodeStyleSettings { 7 | protected JteCodeStyleSettings(CodeStyleSettings container) { 8 | super("JteCodeStyleSettings", container); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/format/KteCodeStyleSettings.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.format; 2 | 3 | import com.intellij.psi.codeStyle.CodeStyleSettings; 4 | import com.intellij.psi.codeStyle.CustomCodeStyleSettings; 5 | 6 | public class KteCodeStyleSettings extends CustomCodeStyleSettings { 7 | protected KteCodeStyleSettings(CodeStyleSettings container) { 8 | super("KteCodeStyleSettings", container); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/parsers/ElseTokenParser.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing.parsers; 2 | 3 | import org.jusecase.jte.intellij.language.parsing.Lexer; 4 | 5 | public class ElseTokenParser extends AbstractTokenParser { 6 | 7 | public ElseTokenParser(Lexer lexer) { 8 | super(lexer); 9 | } 10 | 11 | @Override 12 | public boolean hasToken(int position) { 13 | return hasToken(position, "@else", lexer.tokens.ELSE()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/parsers/EndIfTokenParser.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing.parsers; 2 | 3 | import org.jusecase.jte.intellij.language.parsing.Lexer; 4 | 5 | public class EndIfTokenParser extends AbstractTokenParser { 6 | 7 | public EndIfTokenParser(Lexer lexer) { 8 | super(lexer); 9 | } 10 | 11 | @Override 12 | public boolean hasToken(int position) { 13 | return hasToken(position, "@endif", lexer.tokens.ENDIF()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/parsers/EndForTokenParser.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing.parsers; 2 | 3 | import org.jusecase.jte.intellij.language.parsing.Lexer; 4 | 5 | public class EndForTokenParser extends AbstractTokenParser { 6 | 7 | public EndForTokenParser(Lexer lexer) { 8 | super(lexer); 9 | } 10 | 11 | @Override 12 | public boolean hasToken(int position) { 13 | return hasToken(position, "@endfor", lexer.tokens.ENDFOR()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/JteTemplateDataElementType.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language; 2 | 3 | import com.intellij.lang.Language; 4 | import com.intellij.psi.templateLanguages.TemplateDataElementType; 5 | import org.jusecase.jte.intellij.language.parsing.TokenTypes; 6 | 7 | public class JteTemplateDataElementType extends TemplateDataElementType { 8 | public JteTemplateDataElementType(Language language, TokenTypes tokenTypes) { 9 | super("JTE_TEMPLATE_DATA_HTML", language, tokenTypes.HTML_CONTENT(), tokenTypes.OUTER_ELEMENT_TYPE()); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/JteLanguage.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language; 2 | 3 | import com.intellij.lang.Language; 4 | import com.intellij.psi.templateLanguages.TemplateLanguage; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | public class JteLanguage extends Language implements TemplateLanguage { 8 | public static final JteLanguage INSTANCE = new JteLanguage(); 9 | 10 | private JteLanguage() { 11 | super("JavaTemplateEngine"); 12 | } 13 | 14 | @Override 15 | @NotNull 16 | public String getDisplayName() { 17 | return "jte"; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/KteLanguage.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language; 2 | 3 | import com.intellij.lang.Language; 4 | import com.intellij.psi.templateLanguages.TemplateLanguage; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | public class KteLanguage extends Language implements TemplateLanguage { 8 | public static final KteLanguage INSTANCE = new KteLanguage(); 9 | 10 | private KteLanguage() { 11 | super("KotlinTemplateEngine"); 12 | } 13 | 14 | @Override 15 | @NotNull 16 | public String getDisplayName() { 17 | return "kte"; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/file/CreateJteFileAction.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.file; 2 | 3 | import com.intellij.ide.actions.CreateFileAction; 4 | import org.jetbrains.annotations.Nullable; 5 | import org.jusecase.jte.intellij.language.JteIcons; 6 | 7 | public class CreateJteFileAction extends CreateFileAction { 8 | 9 | public CreateJteFileAction() { 10 | super(() -> "jte Template", () -> "Create jte Template", () -> JteIcons.ICON); 11 | } 12 | 13 | @Override 14 | protected @Nullable String getDefaultExtension() { 15 | return "jte"; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/file/CreateKteFileAction.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.file; 2 | 3 | import com.intellij.ide.actions.CreateFileAction; 4 | import org.jetbrains.annotations.Nullable; 5 | import org.jusecase.jte.intellij.language.JteIcons; 6 | 7 | public class CreateKteFileAction extends CreateFileAction { 8 | 9 | public CreateKteFileAction() { 10 | super(() -> "kte Template", () -> "Create kte Template", () -> JteIcons.ICON); 11 | } 12 | 13 | @Override 14 | protected @Nullable String getDefaultExtension() { 15 | return "kte"; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/JteElementType.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing; 2 | 3 | import com.intellij.psi.tree.IElementType; 4 | import org.jetbrains.annotations.NonNls; 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jusecase.jte.intellij.language.JteLanguage; 7 | 8 | public class JteElementType extends IElementType { 9 | public JteElementType(@NotNull @NonNls String debugName) { 10 | super(debugName, JteLanguage.INSTANCE); 11 | } 12 | 13 | @Override 14 | public String toString() { 15 | return "Jte " + super.toString(); 16 | } 17 | } -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/KteElementType.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing; 2 | 3 | import com.intellij.psi.tree.IElementType; 4 | import org.jetbrains.annotations.NonNls; 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jusecase.jte.intellij.language.KteLanguage; 7 | 8 | public class KteElementType extends IElementType { 9 | public KteElementType(@NotNull @NonNls String debugName) { 10 | super(debugName, KteLanguage.INSTANCE); 11 | } 12 | 13 | @Override 14 | public String toString() { 15 | return "Kte " + super.toString(); 16 | } 17 | } -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/parsers/CommaTokenParser.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing.parsers; 2 | 3 | import org.jusecase.jte.intellij.language.parsing.Lexer; 4 | 5 | public class CommaTokenParser extends AbstractTokenParser { 6 | public CommaTokenParser(Lexer lexer) { 7 | super(lexer); 8 | } 9 | 10 | @Override 11 | public boolean hasToken(int position) { 12 | if (lexer.getCurrentState() == Lexer.CONTENT_STATE_PARAM_NAME) { 13 | return hasToken(position, ",", lexer.tokens.COMMA()); 14 | } 15 | return false; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/org/jusecase/jte/intellij/language/parsing/KteLexerTest.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.jusecase.jte.intellij.language.parsing.KteTokenTypes.*; 6 | 7 | public class KteLexerTest extends LexerTest { 8 | 9 | public KteLexerTest() { 10 | super(new KteLexer()); 11 | } 12 | 13 | @Test 14 | public void param_nativeDefaultValueSupport() { 15 | givenInput("@param x = 32"); 16 | thenTokensAre(PARAM, "@param", 17 | WHITESPACE, " ", 18 | JAVA_INJECTION, "x = 32"); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/format/JteFormattingModelBuilder.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.format; 2 | 3 | import com.intellij.psi.PsiFile; 4 | import org.jusecase.jte.intellij.language.parsing.JteTokenTypes; 5 | import org.jusecase.jte.intellij.language.psi.JtePsiFile; 6 | 7 | public class JteFormattingModelBuilder extends FormattingModelBuilderBase { 8 | 9 | protected JteFormattingModelBuilder() { 10 | super(JteTokenTypes.INSTANCE); 11 | } 12 | 13 | @Override 14 | protected boolean isTemplateFile(PsiFile file) { 15 | return file instanceof JtePsiFile; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/format/KteFormattingModelBuilder.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.format; 2 | 3 | import com.intellij.psi.PsiFile; 4 | import org.jusecase.jte.intellij.language.parsing.KteTokenTypes; 5 | import org.jusecase.jte.intellij.language.psi.KtePsiFile; 6 | 7 | public class KteFormattingModelBuilder extends FormattingModelBuilderBase { 8 | 9 | protected KteFormattingModelBuilder() { 10 | super(KteTokenTypes.INSTANCE); 11 | } 12 | 13 | @Override 14 | protected boolean isTemplateFile(PsiFile file) { 15 | return file instanceof KtePsiFile; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/parsers/IfTokenParser.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing.parsers; 2 | 3 | import org.jusecase.jte.intellij.language.parsing.Lexer; 4 | 5 | public class IfTokenParser extends AbstractTokenParser { 6 | 7 | public IfTokenParser(Lexer lexer) { 8 | super(lexer); 9 | } 10 | 11 | @Override 12 | public boolean hasToken(int position) { 13 | if (hasToken(position, "@if", lexer.tokens.IF())) { 14 | lexer.setCurrentState(Lexer.CONTENT_STATE_IF_BEGIN); 15 | return true; 16 | } 17 | return false; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/parsers/ForTokenParser.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing.parsers; 2 | 3 | import org.jusecase.jte.intellij.language.parsing.Lexer; 4 | 5 | public class ForTokenParser extends AbstractTokenParser { 6 | 7 | public ForTokenParser(Lexer lexer) { 8 | super(lexer); 9 | } 10 | 11 | @Override 12 | public boolean hasToken(int position) { 13 | if (hasToken(position, "@for", lexer.tokens.FOR())) { 14 | lexer.setCurrentState(Lexer.CONTENT_STATE_FOR_BEGIN); 15 | return true; 16 | } 17 | return false; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/parsers/EndContentTokenParser.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing.parsers; 2 | 3 | import org.jusecase.jte.intellij.language.parsing.Lexer; 4 | 5 | public class EndContentTokenParser extends AbstractTokenParser { 6 | 7 | public EndContentTokenParser(Lexer lexer) { 8 | super(lexer); 9 | } 10 | 11 | @Override 12 | public boolean hasToken(int position) { 13 | if (lexer.isInContentBlock() && hasToken(position, "`", lexer.tokens.CONTENT_END())) { 14 | lexer.popPreviousState(); 15 | return true; 16 | } 17 | return false; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/completion/JteCompletionContributor.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.completion; 2 | 3 | import com.intellij.codeInsight.completion.CompletionContributor; 4 | import org.jusecase.jte.intellij.language.parsing.JteTokenTypes; 5 | 6 | import static com.intellij.patterns.PlatformPatterns.psiElement; 7 | 8 | 9 | public class JteCompletionContributor extends CompletionContributor { 10 | public JteCompletionContributor() { 11 | extend(null, psiElement(JteTokenTypes.TEMPLATE_NAME), new JteTemplateCompletionProvider()); 12 | extend(null, psiElement(), new JteTemplateParamCompletionProvider(false)); 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/completion/KteCompletionContributor.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.completion; 2 | 3 | import com.intellij.codeInsight.completion.CompletionContributor; 4 | import org.jusecase.jte.intellij.language.parsing.KteTokenTypes; 5 | 6 | import static com.intellij.patterns.PlatformPatterns.psiElement; 7 | 8 | 9 | public class KteCompletionContributor extends CompletionContributor { 10 | public KteCompletionContributor() { 11 | extend(null, psiElement(KteTokenTypes.TEMPLATE_NAME), new KteTemplateCompletionProvider()); 12 | extend(null, psiElement(), new KteTemplateParamCompletionProvider(false)); 13 | } 14 | } -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/parsers/ParamTokenParser.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing.parsers; 2 | 3 | import org.jusecase.jte.intellij.language.parsing.Lexer; 4 | 5 | public class ParamTokenParser extends AbstractTokenParser { 6 | 7 | public ParamTokenParser(Lexer lexer) { 8 | super(lexer); 9 | } 10 | 11 | @Override 12 | public boolean hasToken(int position) { 13 | if (!lexer.isImportOrParamIgnored() && hasToken(position, "@param", lexer.tokens.PARAM())) { 14 | lexer.setCurrentState(Lexer.CONTENT_STATE_PARAM_BEGIN); 15 | return true; 16 | } 17 | return false; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/parsers/ImportTokenParser.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing.parsers; 2 | 3 | import org.jusecase.jte.intellij.language.parsing.Lexer; 4 | 5 | public class ImportTokenParser extends AbstractTokenParser { 6 | 7 | public ImportTokenParser(Lexer lexer) { 8 | super(lexer); 9 | } 10 | 11 | @Override 12 | public boolean hasToken(int position) { 13 | if (!lexer.isImportOrParamIgnored() && hasToken(position, "@import", lexer.tokens.IMPORT())) { 14 | lexer.setCurrentState(Lexer.CONTENT_STATE_IMPORT_BEGIN); 15 | return true; 16 | } 17 | return false; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/parsers/TemplateTokenParser.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing.parsers; 2 | 3 | import org.jusecase.jte.intellij.language.parsing.Lexer; 4 | 5 | public class TemplateTokenParser extends AbstractTokenParser { 6 | 7 | public TemplateTokenParser(Lexer lexer) { 8 | super(lexer); 9 | } 10 | 11 | @Override 12 | public boolean hasToken(int position) { 13 | if (isBeginOf(position + "@template".length(), '.') && hasToken(position, "@template", lexer.tokens.TEMPLATE())) { 14 | lexer.setCurrentState(Lexer.CONTENT_STATE_TEMPLATE_BEGIN); 15 | return true; 16 | } 17 | return false; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/parsers/ElseIfTokenParser.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing.parsers; 2 | 3 | import org.jusecase.jte.intellij.language.parsing.Lexer; 4 | import org.jusecase.jte.intellij.language.parsing.JteTokenTypes; 5 | 6 | public class ElseIfTokenParser extends AbstractTokenParser { 7 | 8 | public ElseIfTokenParser(Lexer lexer) { 9 | super(lexer); 10 | } 11 | 12 | @Override 13 | public boolean hasToken(int position) { 14 | if (hasToken(position, "@elseif", lexer.tokens.ELSEIF())) { 15 | lexer.setCurrentState(Lexer.CONTENT_STATE_ELSEIF_BEGIN); 16 | return true; 17 | } 18 | return false; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/JteParser.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.intellij.lang.PsiBuilder; 5 | import com.intellij.lang.PsiParser; 6 | import com.intellij.psi.tree.IElementType; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public class JteParser implements PsiParser { 10 | @NotNull 11 | @Override 12 | public ASTNode parse(@NotNull IElementType root, @NotNull PsiBuilder builder) { 13 | final PsiBuilder.Marker rootMarker = builder.mark(); 14 | 15 | new Parsing(builder, JteTokenTypes.INSTANCE).parse(); 16 | 17 | rootMarker.done(root); 18 | 19 | return builder.getTreeBuilt(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/KteParser.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.intellij.lang.PsiBuilder; 5 | import com.intellij.lang.PsiParser; 6 | import com.intellij.psi.tree.IElementType; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public class KteParser implements PsiParser { 10 | @NotNull 11 | @Override 12 | public ASTNode parse(@NotNull IElementType root, @NotNull PsiBuilder builder) { 13 | final PsiBuilder.Marker rootMarker = builder.mark(); 14 | 15 | new Parsing(builder, KteTokenTypes.INSTANCE).parse(); 16 | 17 | rootMarker.done(root); 18 | 19 | return builder.getTreeBuilt(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/convert/JteConvertNotification.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.convert; 2 | 3 | import com.intellij.notification.*; 4 | import com.intellij.openapi.project.Project; 5 | 6 | public class JteConvertNotification { 7 | private static final NotificationGroup STICKY_GROUP = NotificationGroupManager.getInstance().getNotificationGroup("org.jusecase.jte.intellij.language.convert.notification"); 8 | 9 | public static void error(Project project, String title, String content) { 10 | Notification notification = STICKY_GROUP.createNotification(content, NotificationType.ERROR); 11 | notification.setTitle(title); 12 | Notifications.Bus.notify(notification, project); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/completion/JteContext.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.completion; 2 | 3 | import com.intellij.codeInsight.template.TemplateActionContext; 4 | import com.intellij.codeInsight.template.TemplateContextType; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | public class JteContext extends TemplateContextType { 8 | @SuppressWarnings("DialogTitleCapitalization") // jte is lower case by default 9 | protected JteContext() { 10 | super( "jte"); 11 | } 12 | 13 | @Override 14 | public boolean isInContext(@NotNull TemplateActionContext templateActionContext) { 15 | String name = templateActionContext.getFile().getName(); 16 | return name.endsWith(".jte") || name.endsWith(".kte"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/parsers/CommentTokenParser.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing.parsers; 2 | 3 | import org.jusecase.jte.intellij.language.parsing.Lexer; 4 | 5 | public class CommentTokenParser extends AbstractTokenParser { 6 | public CommentTokenParser(Lexer lexer) { 7 | super(lexer); 8 | } 9 | 10 | @Override 11 | public boolean hasToken(int position) { 12 | if (!isBeginOf(position, "<%--")) { 13 | return false; 14 | } 15 | 16 | int start = position; 17 | position += 4; 18 | 19 | while (position < myEndOffset && !isEndOf(position, "--%>")) { 20 | position++; 21 | } 22 | 23 | myTokenInfo.updateData(start, position, lexer.tokens.COMMENT()); 24 | return true; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/JteHighlightVisitor.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language; 2 | 3 | import com.intellij.codeInsight.daemon.impl.analysis.HighlightVisitorImpl; 4 | import com.intellij.psi.PsiFile; 5 | import com.intellij.psi.PsiJavaFile; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | public class JteHighlightVisitor extends HighlightVisitorImpl { 9 | @Override 10 | public boolean suitableForFile(@NotNull PsiFile file) { 11 | if (super.suitableForFile(file)) { 12 | return false; 13 | } 14 | 15 | return file.getName().endsWith(".jte") && file instanceof PsiJavaFile; 16 | } 17 | 18 | @SuppressWarnings("MethodDoesntCallSuperMethod") 19 | @NotNull 20 | @Override 21 | public HighlightVisitorImpl clone() { 22 | return new JteHighlightVisitor(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/parsers/TemplateNameTokenParser.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing.parsers; 2 | 3 | import org.jusecase.jte.intellij.language.parsing.Lexer; 4 | 5 | public class TemplateNameTokenParser extends AbstractTokenParser { 6 | 7 | public TemplateNameTokenParser(Lexer lexer) { 8 | super(lexer); 9 | } 10 | 11 | @Override 12 | public boolean hasToken(int position) { 13 | if (lexer.getCurrentState() == Lexer.CONTENT_STATE_TEMPLATE_BEGIN || lexer.getCurrentState() == Lexer.CONTENT_STATE_TEMPLATE_NAME_BEGIN) { 14 | if (hasToken(position, ".", lexer.tokens.NAME_SEPARATOR())) { 15 | lexer.setCurrentState(Lexer.CONTENT_STATE_TEMPLATE_NAME_BEGIN); 16 | return true; 17 | } 18 | } 19 | 20 | return false; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/JteFileViewProviderFactory.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language; 2 | 3 | import com.intellij.lang.Language; 4 | import com.intellij.openapi.vfs.VirtualFile; 5 | import com.intellij.psi.FileViewProvider; 6 | import com.intellij.psi.FileViewProviderFactory; 7 | import com.intellij.psi.PsiManager; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jusecase.jte.intellij.language.parsing.JteTokenTypes; 10 | 11 | public class JteFileViewProviderFactory implements FileViewProviderFactory { 12 | @NotNull 13 | @Override 14 | public FileViewProvider createFileViewProvider(@NotNull VirtualFile file, Language language, @NotNull PsiManager manager, boolean eventSystemEnabled) { 15 | return new JteFileViewProvider(manager, file, eventSystemEnabled, JteLanguage.INSTANCE, JteTokenTypes.INSTANCE); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/KteFileViewProviderFactory.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language; 2 | 3 | import com.intellij.lang.Language; 4 | import com.intellij.openapi.vfs.VirtualFile; 5 | import com.intellij.psi.FileViewProvider; 6 | import com.intellij.psi.FileViewProviderFactory; 7 | import com.intellij.psi.PsiManager; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jusecase.jte.intellij.language.parsing.KteTokenTypes; 10 | 11 | public class KteFileViewProviderFactory implements FileViewProviderFactory { 12 | @NotNull 13 | @Override 14 | public FileViewProvider createFileViewProvider(@NotNull VirtualFile file, Language language, @NotNull PsiManager manager, boolean eventSystemEnabled) { 15 | return new JteFileViewProvider(manager, file, eventSystemEnabled, KteLanguage.INSTANCE, KteTokenTypes.INSTANCE); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/JteCommenter.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language; 2 | 3 | import com.intellij.lang.Commenter; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | public class JteCommenter implements Commenter { 7 | @Nullable 8 | @Override 9 | public String getLineCommentPrefix() { 10 | return null; 11 | } 12 | 13 | @Nullable 14 | @Override 15 | public String getBlockCommentPrefix() { 16 | return "<%--"; 17 | } 18 | 19 | @Nullable 20 | @Override 21 | public String getBlockCommentSuffix() { 22 | return "--%>"; 23 | } 24 | 25 | @Nullable 26 | @Override 27 | public String getCommentedBlockCommentPrefix() { 28 | return null; 29 | } 30 | 31 | @Nullable 32 | @Override 33 | public String getCommentedBlockCommentSuffix() { 34 | return null; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/parsers/RawTokenParser.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing.parsers; 2 | 3 | import org.jusecase.jte.intellij.language.parsing.Lexer; 4 | 5 | public class RawTokenParser extends AbstractTokenParser { 6 | public RawTokenParser(Lexer lexer) { 7 | super(lexer); 8 | } 9 | 10 | @Override 11 | public boolean hasToken(int position) { 12 | if (lexer.isInHtmlState() && hasToken(position, "@raw", lexer.tokens.RAW())) { 13 | lexer.setCurrentState(Lexer.CONTENT_STATE_RAW); 14 | return true; 15 | } 16 | 17 | int state = lexer.getCurrentState(); 18 | if (state == Lexer.CONTENT_STATE_RAW && hasToken(position, "@endraw", lexer.tokens.ENDRAW())) { 19 | lexer.setCurrentState(Lexer.CONTENT_STATE_HTML); 20 | return true; 21 | } 22 | 23 | return false; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiElement.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.extapi.psi.ASTWrapperPsiElement; 4 | import com.intellij.lang.ASTNode; 5 | import com.intellij.navigation.ItemPresentation; 6 | import com.intellij.navigation.ItemPresentationProviders; 7 | import com.intellij.psi.PsiReference; 8 | import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | public class JtePsiElement extends ASTWrapperPsiElement { 12 | public JtePsiElement(@NotNull ASTNode node) { 13 | super(node); 14 | } 15 | 16 | @Override 17 | public ItemPresentation getPresentation() { 18 | return ItemPresentationProviders.getItemPresentation(this); 19 | } 20 | 21 | @NotNull 22 | @Override 23 | public PsiReference[] getReferences() { 24 | return ReferenceProvidersRegistry.getReferencesFromProviders(this); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/parsers/StatementTokenParser.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing.parsers; 2 | 3 | import org.jusecase.jte.intellij.language.parsing.Lexer; 4 | 5 | public class StatementTokenParser extends AbstractTokenParser { 6 | 7 | public StatementTokenParser(Lexer lexer) { 8 | super(lexer); 9 | } 10 | 11 | @Override 12 | public boolean hasToken(int position) { 13 | if (lexer.isInHtmlState() && hasToken(position, "!{", lexer.tokens.STATEMENT_BEGIN())) { 14 | lexer.setCurrentState(Lexer.CONTENT_STATE_STATEMENT_BEGIN); 15 | return true; 16 | } 17 | 18 | int state = lexer.getCurrentState(); 19 | if (state == Lexer.CONTENT_STATE_STATEMENT_BEGIN && hasToken(position, "}", lexer.tokens.STATEMENT_END())) { 20 | lexer.setCurrentState(Lexer.CONTENT_STATE_STATEMENT_END); 21 | return true; 22 | } 23 | 24 | return false; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/completion/JteCompletionContributorForJava.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.completion; 2 | 3 | import com.intellij.codeInsight.completion.CompletionContributor; 4 | import com.intellij.patterns.PatternCondition; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.util.ProcessingContext; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import static com.intellij.patterns.PlatformPatterns.psiElement; 10 | 11 | 12 | public class JteCompletionContributorForJava extends CompletionContributor { 13 | public JteCompletionContributorForJava() { 14 | extend(null, psiElement().with(new PatternCondition("JteFile") { 15 | @Override 16 | public boolean accepts(@NotNull PsiElement element, ProcessingContext context) { 17 | return element.getContainingFile() != null && element.getContainingFile().getName().endsWith(".jte"); 18 | } 19 | }), new JteTemplateParamCompletionProvider(true)); 20 | } 21 | } -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiFile.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.extapi.psi.PsiFileBase; 4 | import com.intellij.lang.Language; 5 | import com.intellij.openapi.fileTypes.FileType; 6 | import com.intellij.psi.FileViewProvider; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jusecase.jte.intellij.language.JteFileType; 9 | import org.jusecase.jte.intellij.language.JteLanguage; 10 | 11 | public class JtePsiFile extends PsiFileBase { 12 | public JtePsiFile(@NotNull FileViewProvider viewProvider) { 13 | this(viewProvider, JteLanguage.INSTANCE); 14 | } 15 | 16 | public JtePsiFile(@NotNull FileViewProvider viewProvider, Language lang) { 17 | super(viewProvider, lang); 18 | } 19 | 20 | @Override 21 | @NotNull 22 | public FileType getFileType() { 23 | return JteFileType.INSTANCE; 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | return "JteFile:" + getName(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/KtePsiFile.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.extapi.psi.PsiFileBase; 4 | import com.intellij.lang.Language; 5 | import com.intellij.openapi.fileTypes.FileType; 6 | import com.intellij.psi.FileViewProvider; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jusecase.jte.intellij.language.KteFileType; 9 | import org.jusecase.jte.intellij.language.KteLanguage; 10 | 11 | public class KtePsiFile extends PsiFileBase { 12 | public KtePsiFile(@NotNull FileViewProvider viewProvider) { 13 | this(viewProvider, KteLanguage.INSTANCE); 14 | } 15 | 16 | public KtePsiFile(@NotNull FileViewProvider viewProvider, Language lang) { 17 | super(viewProvider, lang); 18 | } 19 | 20 | @Override 21 | @NotNull 22 | public FileType getFileType() { 23 | return KteFileType.INSTANCE; 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | return "KteFile:" + getName(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/JteFileType.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language; 2 | 3 | import com.intellij.openapi.fileTypes.LanguageFileType; 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | import javax.swing.*; 8 | 9 | public class JteFileType extends LanguageFileType { 10 | 11 | public static final JteFileType INSTANCE = new JteFileType(); 12 | 13 | private JteFileType() { 14 | super(JteLanguage.INSTANCE); 15 | } 16 | 17 | @NotNull 18 | @Override 19 | public String getName() { 20 | return "Java Template Engine File"; 21 | } 22 | 23 | @NotNull 24 | @Override 25 | public String getDescription() { 26 | return "Java Template Engine File"; 27 | } 28 | 29 | @NotNull 30 | @Override 31 | public String getDefaultExtension() { 32 | return "jte"; 33 | } 34 | 35 | @Nullable 36 | @Override 37 | public Icon getIcon() { 38 | return JteIcons.ICON; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/KteFileType.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language; 2 | 3 | import com.intellij.openapi.fileTypes.LanguageFileType; 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | import javax.swing.*; 8 | 9 | public class KteFileType extends LanguageFileType { 10 | 11 | public static final KteFileType INSTANCE = new KteFileType(); 12 | 13 | private KteFileType() { 14 | super(KteLanguage.INSTANCE); 15 | } 16 | 17 | @NotNull 18 | @Override 19 | public String getName() { 20 | return "Kotlin Template Engine File"; 21 | } 22 | 23 | @NotNull 24 | @Override 25 | public String getDescription() { 26 | return "Kotlin Template Engine File"; 27 | } 28 | 29 | @NotNull 30 | @Override 31 | public String getDefaultExtension() { 32 | return "kte"; 33 | } 34 | 35 | @Nullable 36 | @Override 37 | public Icon getIcon() { 38 | return JteIcons.ICON; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/completion/KteCompletionContributorForKotlin.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.completion; 2 | 3 | import com.intellij.codeInsight.completion.CompletionContributor; 4 | import com.intellij.patterns.PatternCondition; 5 | import com.intellij.psi.PsiElement; 6 | import com.intellij.util.ProcessingContext; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import static com.intellij.patterns.PlatformPatterns.psiElement; 10 | 11 | 12 | public class KteCompletionContributorForKotlin extends CompletionContributor { 13 | public KteCompletionContributorForKotlin() { 14 | extend(null, psiElement().with(new PatternCondition("KteFile") { 15 | @Override 16 | public boolean accepts(@NotNull PsiElement element, ProcessingContext context) { 17 | return element.getContainingFile() != null && element.getContainingFile().getName().endsWith(".kte"); 18 | } 19 | }), new KteTemplateParamCompletionProvider(true)); 20 | } 21 | } -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/parsers/EqualsTokenParser.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing.parsers; 2 | 3 | import org.jusecase.jte.intellij.language.parsing.Lexer; 4 | 5 | public class EqualsTokenParser extends AbstractTokenParser { 6 | 7 | public EqualsTokenParser(Lexer lexer) { 8 | super(lexer); 9 | } 10 | 11 | @Override 12 | public boolean hasToken(int position) { 13 | if (lexer.getCurrentState() == Lexer.CONTENT_STATE_PARAM_DEFAULT_VALUE) { 14 | return hasToken(position, "=", lexer.tokens.EQUALS()); 15 | } 16 | 17 | if (lexer.getCurrentState() == Lexer.CONTENT_STATE_PARAM_NAME) { 18 | if (hasToken(position, "=", lexer.tokens.EQUALS())) { 19 | if (lexer.getCurrentCount() == Lexer.CONTENT_COUNT_PARAM_NAME_TEMPLATE) { 20 | lexer.setCurrentCount(Lexer.CONTENT_COUNT_PARAM_NAME_TEMPLATE_DONE); 21 | return true; 22 | } 23 | } 24 | } 25 | 26 | return false; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/parsers/ForConditionTokenParser.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing.parsers; 2 | 3 | import org.jusecase.jte.intellij.language.parsing.Lexer; 4 | 5 | public class ForConditionTokenParser extends AbstractTokenParser { 6 | 7 | public ForConditionTokenParser(Lexer lexer) { 8 | super(lexer); 9 | } 10 | 11 | @Override 12 | public boolean hasToken(int position) { 13 | if (lexer.getCurrentState() == Lexer.CONTENT_STATE_FOR_BEGIN) { 14 | if (hasToken(position, "(", lexer.tokens.CONDITION_BEGIN())) { 15 | lexer.setCurrentState(Lexer.CONTENT_STATE_FOR_CONDITION); 16 | return true; 17 | } 18 | } else if (lexer.getCurrentState() == Lexer.CONTENT_STATE_FOR_CONDITION && lexer.getCurrentCount() <= 0) { 19 | if (hasToken(position, ")", lexer.tokens.CONDITION_END())) { 20 | lexer.setCurrentState(Lexer.CONTENT_STATE_FOR_END); 21 | return true; 22 | } 23 | } 24 | 25 | return false; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/JteHighlighterProvider.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language; 2 | 3 | import com.intellij.ide.highlighter.JavaFileType; 4 | import com.intellij.openapi.editor.colors.EditorColorsScheme; 5 | import com.intellij.openapi.editor.highlighter.EditorHighlighter; 6 | import com.intellij.openapi.fileTypes.EditorHighlighterProvider; 7 | import com.intellij.openapi.fileTypes.FileType; 8 | import com.intellij.openapi.project.Project; 9 | import com.intellij.openapi.vfs.VirtualFile; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | import org.jusecase.jte.intellij.language.parsing.JteTokenTypes; 13 | 14 | public class JteHighlighterProvider implements EditorHighlighterProvider { 15 | @Override 16 | public EditorHighlighter getEditorHighlighter(@Nullable Project project, @NotNull FileType fileType, @Nullable VirtualFile virtualFile, @NotNull EditorColorsScheme colors) { 17 | return new TemplateHighlighter(project, virtualFile, colors, JavaFileType.INSTANCE, JteTokenTypes.INSTANCE, new JteHighlighter()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/KteHighlighterProvider.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language; 2 | 3 | import com.intellij.openapi.editor.colors.EditorColorsScheme; 4 | import com.intellij.openapi.editor.highlighter.EditorHighlighter; 5 | import com.intellij.openapi.fileTypes.EditorHighlighterProvider; 6 | import com.intellij.openapi.fileTypes.FileType; 7 | import com.intellij.openapi.project.Project; 8 | import com.intellij.openapi.vfs.VirtualFile; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | import org.jetbrains.kotlin.idea.KotlinFileType; 12 | import org.jusecase.jte.intellij.language.parsing.KteTokenTypes; 13 | 14 | public class KteHighlighterProvider implements EditorHighlighterProvider { 15 | @Override 16 | public EditorHighlighter getEditorHighlighter(@Nullable Project project, @NotNull FileType fileType, @Nullable VirtualFile virtualFile, @NotNull EditorColorsScheme colors) { 17 | return new TemplateHighlighter(project, virtualFile, colors, KotlinFileType.INSTANCE, KteTokenTypes.INSTANCE, new KteHighlighter()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/refactoring/JteSearchScope.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.refactoring; 2 | 3 | import com.intellij.openapi.module.Module; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.openapi.vfs.VirtualFile; 6 | import com.intellij.psi.search.GlobalSearchScope; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public class JteSearchScope extends GlobalSearchScope { 10 | 11 | private final GlobalSearchScope projectScope; 12 | 13 | public JteSearchScope(Project project) { 14 | projectScope = GlobalSearchScope.projectScope(project); 15 | } 16 | 17 | @Override 18 | public boolean isSearchInModuleContent(@NotNull Module aModule) { 19 | return true; 20 | } 21 | 22 | @Override 23 | public boolean isSearchInLibraries() { 24 | return false; 25 | } 26 | 27 | @Override 28 | public boolean contains(@NotNull VirtualFile file) { 29 | if (file.getName().endsWith(".jte")) { 30 | return projectScope.contains(file); 31 | } 32 | 33 | return false; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/refactoring/KteSearchScope.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.refactoring; 2 | 3 | import com.intellij.openapi.module.Module; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.openapi.vfs.VirtualFile; 6 | import com.intellij.psi.search.GlobalSearchScope; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public class KteSearchScope extends GlobalSearchScope { 10 | 11 | private final GlobalSearchScope projectScope; 12 | 13 | public KteSearchScope(Project project) { 14 | projectScope = GlobalSearchScope.projectScope(project); 15 | } 16 | 17 | @Override 18 | public boolean isSearchInModuleContent(@NotNull Module aModule) { 19 | return true; 20 | } 21 | 22 | @Override 23 | public boolean isSearchInLibraries() { 24 | return false; 25 | } 26 | 27 | @Override 28 | public boolean contains(@NotNull VirtualFile file) { 29 | if (file.getName().endsWith(".kte")) { 30 | return projectScope.contains(file); 31 | } 32 | 33 | return false; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/parsers/IfConditionTokenParser.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing.parsers; 2 | 3 | import org.jusecase.jte.intellij.language.parsing.Lexer; 4 | 5 | public class IfConditionTokenParser extends AbstractTokenParser { 6 | 7 | public IfConditionTokenParser(Lexer lexer) { 8 | super(lexer); 9 | } 10 | 11 | @Override 12 | public boolean hasToken(int position) { 13 | if (lexer.getCurrentState() == Lexer.CONTENT_STATE_IF_BEGIN) { 14 | if (hasToken(position, "(", lexer.tokens.CONDITION_BEGIN())) { 15 | lexer.setCurrentState(Lexer.CONTENT_STATE_IF_CONDITION); 16 | return true; 17 | } 18 | } else if (lexer.getCurrentState() == Lexer.CONTENT_STATE_IF_CONDITION && lexer.getCurrentCount() <= 0) { 19 | if (hasToken(position, ")", lexer.tokens.CONDITION_END())) { 20 | lexer.setCurrentCount(0); 21 | lexer.setCurrentState(Lexer.CONTENT_STATE_IF_END); 22 | return true; 23 | } 24 | } 25 | 26 | return false; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/parsers/TemplateParamsTokenParser.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing.parsers; 2 | 3 | import org.jusecase.jte.intellij.language.parsing.Lexer; 4 | 5 | public class TemplateParamsTokenParser extends AbstractTokenParser { 6 | 7 | public TemplateParamsTokenParser(Lexer lexer) { 8 | super(lexer); 9 | } 10 | 11 | @Override 12 | public boolean hasToken(int position) { 13 | if (lexer.getCurrentState() == Lexer.CONTENT_STATE_TEMPLATE_NAME_BEGIN) { 14 | if (hasToken(position, "(", lexer.tokens.PARAMS_BEGIN())) { 15 | lexer.setCurrentState(Lexer.CONTENT_STATE_TEMPLATE_PARAMS); 16 | return true; 17 | } 18 | } else if (lexer.getCurrentState() == Lexer.CONTENT_STATE_TEMPLATE_PARAMS && lexer.getCurrentCount() <= 0) { 19 | if (hasToken(position, ")", lexer.tokens.PARAMS_END())) { 20 | lexer.setCurrentCount(0); 21 | lexer.setCurrentState(Lexer.CONTENT_STATE_TEMPLATE_END); 22 | return true; 23 | } 24 | } 25 | 26 | return false; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/parsers/ElseIfConditionTokenParser.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing.parsers; 2 | 3 | import org.jusecase.jte.intellij.language.parsing.Lexer; 4 | 5 | public class ElseIfConditionTokenParser extends AbstractTokenParser { 6 | 7 | public ElseIfConditionTokenParser(Lexer lexer) { 8 | super(lexer); 9 | } 10 | 11 | @Override 12 | public boolean hasToken(int position) { 13 | if (lexer.getCurrentState() == Lexer.CONTENT_STATE_ELSEIF_BEGIN) { 14 | if (hasToken(position, "(", lexer.tokens.CONDITION_BEGIN())) { 15 | lexer.setCurrentState(Lexer.CONTENT_STATE_ELSEIF_CONDITION); 16 | return true; 17 | } 18 | } else if (lexer.getCurrentState() == Lexer.CONTENT_STATE_ELSEIF_CONDITION && lexer.getCurrentCount() <= 0) { 19 | if (hasToken(position, ")", lexer.tokens.CONDITION_END())) { 20 | lexer.setCurrentCount(0); 21 | lexer.setCurrentState(Lexer.CONTENT_STATE_ELSEIF_END); 22 | return true; 23 | } 24 | } 25 | 26 | return false; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/parsers/OutputTokenParser.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing.parsers; 2 | 3 | import org.jusecase.jte.intellij.language.parsing.Lexer; 4 | 5 | public class OutputTokenParser extends AbstractTokenParser { 6 | 7 | public OutputTokenParser(Lexer lexer) { 8 | super(lexer); 9 | } 10 | 11 | @Override 12 | public boolean hasToken(int position) { 13 | if (lexer.isInHtmlState() && hasToken(position, "${", lexer.tokens.OUTPUT_BEGIN())) { 14 | lexer.setCurrentState(Lexer.CONTENT_STATE_OUTPUT_BEGIN); 15 | return true; 16 | } 17 | 18 | if (lexer.isInHtmlState() && hasToken(position, "$unsafe{", lexer.tokens.OUTPUT_BEGIN())) { 19 | lexer.setCurrentState(Lexer.CONTENT_STATE_OUTPUT_BEGIN); 20 | return true; 21 | } 22 | 23 | int state = lexer.getCurrentState(); 24 | if (state == Lexer.CONTENT_STATE_OUTPUT_BEGIN && hasToken(position, "}", lexer.tokens.OUTPUT_END())) { 25 | lexer.setCurrentState(Lexer.CONTENT_STATE_OUTPUT_END); 26 | return true; 27 | } 28 | 29 | return false; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/JteHighlighter.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language; 2 | 3 | import com.intellij.openapi.editor.colors.TextAttributesKey; 4 | import com.intellij.openapi.fileTypes.SyntaxHighlighterBase; 5 | import com.intellij.psi.tree.IElementType; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jusecase.jte.intellij.language.parsing.JteLexer; 8 | import org.jusecase.jte.intellij.language.parsing.Lexer; 9 | import org.jusecase.jte.intellij.language.parsing.JteTokenTypes; 10 | 11 | import java.util.Map; 12 | 13 | public class JteHighlighter extends SyntaxHighlighterBase { 14 | 15 | private static final Map MAPPING = HighlighterMapping.create(JteTokenTypes.INSTANCE); 16 | 17 | @NotNull 18 | @Override 19 | public Lexer getHighlightingLexer() { 20 | return new JteLexer(); 21 | } 22 | 23 | @Override 24 | public TextAttributesKey @NotNull [] getTokenHighlights(IElementType tokenType) { 25 | TextAttributesKey[] attributes = MAPPING.get(tokenType); 26 | if (attributes != null) { 27 | return attributes; 28 | } 29 | return TextAttributesKey.EMPTY_ARRAY; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/KteHighlighter.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language; 2 | 3 | import com.intellij.openapi.editor.colors.TextAttributesKey; 4 | import com.intellij.openapi.fileTypes.SyntaxHighlighterBase; 5 | import com.intellij.psi.tree.IElementType; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jusecase.jte.intellij.language.parsing.KteLexer; 8 | import org.jusecase.jte.intellij.language.parsing.KteTokenTypes; 9 | import org.jusecase.jte.intellij.language.parsing.Lexer; 10 | 11 | import java.util.Map; 12 | 13 | public class KteHighlighter extends SyntaxHighlighterBase { 14 | 15 | private static final Map MAPPING = HighlighterMapping.create(KteTokenTypes.INSTANCE); 16 | 17 | @NotNull 18 | @Override 19 | public Lexer getHighlightingLexer() { 20 | return new KteLexer(); 21 | } 22 | 23 | @Override 24 | public TextAttributesKey @NotNull [] getTokenHighlights(IElementType tokenType) { 25 | TextAttributesKey[] attributes = MAPPING.get(tokenType); 26 | if (attributes != null) { 27 | return attributes; 28 | } 29 | return TextAttributesKey.EMPTY_ARRAY; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time 6 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle 7 | 8 | name: Java CI with Gradle 9 | 10 | on: 11 | push: 12 | branches: [ "main" ] 13 | pull_request: 14 | branches: [ "main" ] 15 | 16 | jobs: 17 | build: 18 | 19 | runs-on: ubuntu-latest 20 | permissions: 21 | contents: read 22 | 23 | steps: 24 | - uses: actions/checkout@v4 25 | - name: Set up JDK 26 | uses: actions/setup-java@v4 27 | with: 28 | java-version: '21' 29 | distribution: 'temurin' 30 | 31 | # Configure Gradle for optimal use in GitHub Actions, including caching of downloaded dependencies. 32 | # See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md 33 | - name: Setup Gradle 34 | uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 35 | 36 | - name: Build with Gradle Wrapper 37 | run: ./gradlew build 38 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/KteBraceMatcher.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language; 2 | 3 | import com.intellij.lang.BracePair; 4 | import com.intellij.lang.PairedBraceMatcher; 5 | import com.intellij.psi.PsiFile; 6 | import com.intellij.psi.tree.IElementType; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | import org.jusecase.jte.intellij.language.parsing.KteTokenTypes; 10 | 11 | public class KteBraceMatcher implements PairedBraceMatcher { 12 | 13 | private final BracePair[] pairs = new BracePair[]{ 14 | new BracePair(KteTokenTypes.IF, KteTokenTypes.ENDIF, true), 15 | new BracePair(KteTokenTypes.FOR, KteTokenTypes.ENDFOR, true), 16 | new BracePair(KteTokenTypes.RAW, KteTokenTypes.ENDRAW, true), 17 | new BracePair(KteTokenTypes.CONTENT_BEGIN, KteTokenTypes.CONTENT_END, true), 18 | }; 19 | 20 | @NotNull 21 | @Override 22 | public BracePair @NotNull [] getPairs() { 23 | return pairs; 24 | } 25 | 26 | @Override 27 | public boolean isPairedBracesAllowedBeforeType(@NotNull IElementType lbraceType, @Nullable IElementType contextType) { 28 | return false; 29 | } 30 | 31 | @Override 32 | public int getCodeConstructStart(PsiFile file, int openingBraceOffset) { 33 | return openingBraceOffset; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/parsers/ContentBlockTokenParser.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing.parsers; 2 | 3 | import org.jusecase.jte.intellij.language.parsing.Lexer; 4 | 5 | public class ContentBlockTokenParser extends AbstractTokenParser { 6 | 7 | public ContentBlockTokenParser(Lexer lexer) { 8 | super(lexer); 9 | } 10 | 11 | @Override 12 | public boolean hasToken(int position) { 13 | if (hasToken(position, "@`", lexer.tokens.CONTENT_BEGIN())) { 14 | if (lexer.getCurrentState() == Lexer.CONTENT_STATE_PARAM_NAME) { 15 | lexer.setCurrentState(Lexer.CONTENT_STATE_TEMPLATE_PARAMS); 16 | } 17 | lexer.pushPreviousState(); 18 | 19 | // To prevent syntax highlighter bugs, see https://intellij-support.jetbrains.com/hc/en-us/community/posts/115000549784-Is-there-any-way-to-start-syntax-highlighting-lexer-for-the-whole-file-instead-of-starting-it-for-the-part-that-have-changed- 20 | // The highlighting lexer is only restarted at points where it returned 0 from getState, so we use the special state CONTENT_STATE_HTML_CONTENT_BLOCK for content blocks, 21 | // which rely on a deque to restore the previous state (they can't handle everything in one integer). 22 | lexer.setCurrentState(Lexer.CONTENT_STATE_HTML_CONTENT_BLOCK); 23 | return true; 24 | } 25 | return false; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/format/JteLanguageCodeStyleSettingsProvider.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.format; 2 | 3 | import com.intellij.application.options.IndentOptionsEditor; 4 | import com.intellij.application.options.SmartIndentOptionsEditor; 5 | import com.intellij.lang.Language; 6 | import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | import org.jusecase.jte.intellij.language.JteLanguage; 10 | 11 | public class JteLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSettingsProvider { 12 | @Override 13 | public @Nullable String getCodeSample(@NotNull SettingsType settingsType) { 14 | return "@import foo.Page\n" + 15 | "\n" + 16 | "@param Page page\n" + 17 | "@param boolean verbose = false\n" + 18 | "\n" + 19 | "
\n" + 20 | "@if(verbose)\n" + 21 | "${page.getVerboseTitle()}\n" + 22 | "@else\n" + 23 | "${page.getTitle()}\n" + 24 | "@endif\n" + 25 | "
\n"; 26 | } 27 | 28 | @Override 29 | public @NotNull Language getLanguage() { 30 | return JteLanguage.INSTANCE; 31 | } 32 | 33 | @Override 34 | public @Nullable IndentOptionsEditor getIndentOptionsEditor() { 35 | return new SmartIndentOptionsEditor(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/format/KteLanguageCodeStyleSettingsProvider.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.format; 2 | 3 | import com.intellij.application.options.IndentOptionsEditor; 4 | import com.intellij.application.options.SmartIndentOptionsEditor; 5 | import com.intellij.lang.Language; 6 | import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | import org.jusecase.jte.intellij.language.KteLanguage; 10 | 11 | public class KteLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSettingsProvider { 12 | @Override 13 | public @Nullable String getCodeSample(@NotNull SettingsType settingsType) { 14 | return "@import foo.Page\n" + 15 | "\n" + 16 | "@param page:Page\n" + 17 | "@param verbose:Boolean = false\n" + 18 | "\n" + 19 | "
\n" + 20 | "@if(verbose)\n" + 21 | "${page.getVerboseTitle()}\n" + 22 | "@else\n" + 23 | "${page.getTitle()}\n" + 24 | "@endif\n" + 25 | "
\n"; 26 | } 27 | 28 | @Override 29 | public @NotNull Language getLanguage() { 30 | return KteLanguage.INSTANCE; 31 | } 32 | 33 | @Override 34 | public @Nullable IndentOptionsEditor getIndentOptionsEditor() { 35 | return new SmartIndentOptionsEditor(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/KtePsiUtil.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.injection.InjectedLanguageManager; 4 | import com.intellij.psi.*; 5 | import com.intellij.psi.util.PsiTreeUtil; 6 | import org.jetbrains.kotlin.psi.*; 7 | import org.jusecase.jte.intellij.language.KteKotlinLanguageInjector; 8 | 9 | public class KtePsiUtil { 10 | 11 | public static KtParameterList resolveParameterList(PsiFile templateFile) { 12 | KtFile kotlinFile = templateFile.getUserData(KteKotlinLanguageInjector.KOTLIN_FILE_KEY); 13 | if (kotlinFile == null) { 14 | // Try to trigger injection and check if kotlin file is there afterwards 15 | InjectedLanguageManager.getInstance(templateFile.getProject()).findInjectedElementAt(templateFile, 0); 16 | kotlinFile = templateFile.getUserData(KteKotlinLanguageInjector.KOTLIN_FILE_KEY); 17 | if (kotlinFile == null) { 18 | return null; 19 | } 20 | } 21 | 22 | KtClass kotlinClass = PsiTreeUtil.findChildOfType(kotlinFile, KtClass.class); 23 | if (kotlinClass == null) { 24 | return null; 25 | } 26 | 27 | KtFunction renderMethod = PsiTreeUtil.findChildOfType(kotlinClass, KtFunction.class); 28 | if (renderMethod == null) { 29 | return null; 30 | } 31 | 32 | return PsiTreeUtil.getChildOfType(renderMethod, KtParameterList.class); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/JteBraceMatcher.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language; 2 | 3 | import com.intellij.lang.BracePair; 4 | import com.intellij.lang.PairedBraceMatcher; 5 | import com.intellij.psi.PsiFile; 6 | import com.intellij.psi.tree.IElementType; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | import org.jusecase.jte.intellij.language.parsing.JteTokenTypes; 10 | 11 | public class JteBraceMatcher implements PairedBraceMatcher { 12 | 13 | private final BracePair[] pairs = new BracePair[]{ 14 | new BracePair(JteTokenTypes.IF, JteTokenTypes.ENDIF, true), 15 | new BracePair(JteTokenTypes.FOR, JteTokenTypes.ENDFOR, true), 16 | new BracePair(JteTokenTypes.RAW, JteTokenTypes.ENDRAW, true), 17 | new BracePair(JteTokenTypes.CONTENT_BEGIN, JteTokenTypes.CONTENT_END, true), 18 | new BracePair(JteTokenTypes.OUTPUT_BEGIN, JteTokenTypes.OUTPUT_END, false), 19 | new BracePair(JteTokenTypes.STATEMENT_BEGIN, JteTokenTypes.STATEMENT_END, false), 20 | }; 21 | 22 | @NotNull 23 | @Override 24 | public BracePair[] getPairs() { 25 | return pairs; 26 | } 27 | 28 | @Override 29 | public boolean isPairedBracesAllowedBeforeType(@NotNull IElementType lbraceType, @Nullable IElementType contextType) { 30 | return false; 31 | } 32 | 33 | @Override 34 | public int getCodeConstructStart(PsiFile file, int openingBraceOffset) { 35 | return openingBraceOffset; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/refactoring/JteUseScopeEnlarger.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.refactoring; 2 | 3 | import com.intellij.psi.PsiElement; 4 | import com.intellij.psi.PsiFile; 5 | import com.intellij.psi.PsiParameter; 6 | import com.intellij.psi.search.SearchScope; 7 | import com.intellij.psi.search.UseScopeEnlarger; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | import org.jetbrains.kotlin.psi.KtParameter; 11 | 12 | public class JteUseScopeEnlarger extends UseScopeEnlarger { 13 | @Nullable 14 | @Override 15 | public SearchScope getAdditionalUseScope(@NotNull PsiElement element) { 16 | if (element instanceof PsiParameter) { 17 | PsiFile containingFile = element.getContainingFile(); 18 | if (containingFile == null) { 19 | return null; 20 | } 21 | 22 | if (!containingFile.getName().endsWith(".jte")) { 23 | return null; 24 | } 25 | 26 | return new JteSearchScope(element.getProject()); 27 | } else if (element instanceof KtParameter) { 28 | PsiFile containingFile = element.getContainingFile(); 29 | if (containingFile == null) { 30 | return null; 31 | } 32 | 33 | if (!containingFile.getName().endsWith(".kte")) { 34 | return null; 35 | } 36 | 37 | return new KteSearchScope(element.getProject()); 38 | } 39 | 40 | return null; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/refactoring/JteFindUsagesProvider.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.refactoring; 2 | 3 | import com.intellij.lang.HelpID; 4 | import com.intellij.lang.findUsages.FindUsagesProvider; 5 | import com.intellij.openapi.util.text.StringUtil; 6 | import com.intellij.psi.PsiElement; 7 | import com.intellij.psi.PsiNamedElement; 8 | import org.jetbrains.annotations.Nls; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | public class JteFindUsagesProvider implements FindUsagesProvider { 13 | @Override 14 | public boolean canFindUsagesFor(@NotNull PsiElement psiElement) { 15 | return psiElement instanceof PsiNamedElement; 16 | } 17 | 18 | @Nullable 19 | @Override 20 | public String getHelpId(@NotNull PsiElement psiElement) { 21 | return HelpID.FIND_OTHER_USAGES; 22 | } 23 | 24 | @Nls 25 | @NotNull 26 | @Override 27 | public String getType(@NotNull PsiElement element) { 28 | return "element"; 29 | } 30 | 31 | @Nls 32 | @NotNull 33 | @Override 34 | public String getDescriptiveName(@NotNull PsiElement element) { 35 | if (element instanceof PsiNamedElement) { 36 | return StringUtil.notNullize(((PsiNamedElement)element).getName()); 37 | } 38 | return element.getText(); 39 | } 40 | 41 | @Nls 42 | @NotNull 43 | @Override 44 | public String getNodeText(@NotNull PsiElement element, boolean useFullName) { 45 | return getDescriptiveName(element); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/refactoring/KteFindUsagesProvider.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.refactoring; 2 | 3 | import com.intellij.lang.HelpID; 4 | import com.intellij.lang.findUsages.FindUsagesProvider; 5 | import com.intellij.openapi.util.text.StringUtil; 6 | import com.intellij.psi.PsiElement; 7 | import com.intellij.psi.PsiNamedElement; 8 | import org.jetbrains.annotations.Nls; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | public class KteFindUsagesProvider implements FindUsagesProvider { 13 | @Override 14 | public boolean canFindUsagesFor(@NotNull PsiElement psiElement) { 15 | return psiElement instanceof PsiNamedElement; 16 | } 17 | 18 | @Nullable 19 | @Override 20 | public String getHelpId(@NotNull PsiElement psiElement) { 21 | return HelpID.FIND_OTHER_USAGES; 22 | } 23 | 24 | @Nls 25 | @NotNull 26 | @Override 27 | public String getType(@NotNull PsiElement element) { 28 | return "element"; 29 | } 30 | 31 | @Nls 32 | @NotNull 33 | @Override 34 | public String getDescriptiveName(@NotNull PsiElement element) { 35 | if (element instanceof PsiNamedElement) { 36 | return StringUtil.notNullize(((PsiNamedElement)element).getName()); 37 | } 38 | return element.getText(); 39 | } 40 | 41 | @Nls 42 | @NotNull 43 | @Override 44 | public String getNodeText(@NotNull PsiElement element, boolean useFullName) { 45 | return getDescriptiveName(element); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/TokenTypes.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing; 2 | 3 | import com.intellij.psi.tree.*; 4 | 5 | public interface TokenTypes { 6 | IElementType HTML_CONTENT(); 7 | IElementType JAVA_CONTENT(); 8 | IElementType BLOCK(); 9 | 10 | IElementType JAVA_INJECTION(); 11 | IElementType EXTRA_JAVA_INJECTION(); 12 | 13 | IElementType OUTER_ELEMENT_TYPE(); 14 | 15 | IElementType IMPORT(); 16 | IElementType PARAM(); 17 | IElementType OUTPUT(); 18 | IElementType OUTPUT_BEGIN(); 19 | IElementType OUTPUT_END(); 20 | IElementType STATEMENT(); 21 | IElementType STATEMENT_BEGIN(); 22 | IElementType STATEMENT_END(); 23 | 24 | IElementType IF(); 25 | IElementType CONDITION_BEGIN(); 26 | IElementType CONDITION_END(); 27 | IElementType ENDIF(); 28 | IElementType ELSE(); 29 | IElementType ELSEIF(); 30 | IElementType FOR(); 31 | IElementType ENDFOR(); 32 | IElementType RAW(); 33 | IElementType ENDRAW(); 34 | 35 | IElementType TEMPLATE(); 36 | IElementType TEMPLATE_NAME(); 37 | IElementType NAME_SEPARATOR(); 38 | IElementType PARAMS_BEGIN(); 39 | IElementType PARAM_NAME(); 40 | IElementType PARAMS_END(); 41 | IElementType CONTENT_BEGIN(); 42 | IElementType CONTENT_END(); 43 | 44 | IElementType COMMENT(); 45 | IElementType COMMENT_CONTENT(); 46 | 47 | IElementType WHITESPACE(); 48 | IElementType EQUALS(); 49 | IElementType COMMA(); 50 | 51 | IElementType STRING(); 52 | 53 | IFileElementType FILE(); 54 | 55 | TokenSet COMMENTS(); 56 | TokenSet STRING_LITERALS(); 57 | TokenSet WHITESPACES(); 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/format/FormattingModelBuilderBase.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.format; 2 | 3 | import com.intellij.formatting.Alignment; 4 | import com.intellij.formatting.Block; 5 | import com.intellij.formatting.Indent; 6 | import com.intellij.formatting.Wrap; 7 | import com.intellij.lang.ASTNode; 8 | import com.intellij.psi.PsiElement; 9 | import com.intellij.psi.codeStyle.CodeStyleSettings; 10 | import com.intellij.psi.formatter.xml.XmlFormattingPolicy; 11 | import com.intellij.xml.template.formatter.AbstractXmlTemplateFormattingModelBuilder; 12 | import org.jetbrains.annotations.Nullable; 13 | import org.jusecase.jte.intellij.language.parsing.TokenTypes; 14 | 15 | public abstract class FormattingModelBuilderBase extends AbstractXmlTemplateFormattingModelBuilder { 16 | 17 | private final TokenTypes tokenTypes; 18 | 19 | protected FormattingModelBuilderBase(TokenTypes tokenTypes) { 20 | this.tokenTypes = tokenTypes; 21 | } 22 | 23 | @Override 24 | public boolean isOuterLanguageElement(PsiElement element) { 25 | return element.getNode().getElementType() == tokenTypes.OUTER_ELEMENT_TYPE(); 26 | } 27 | 28 | @Override 29 | public boolean isMarkupLanguageElement(PsiElement element) { 30 | return element.getNode().getElementType() == tokenTypes.HTML_CONTENT(); 31 | } 32 | 33 | @Override 34 | protected Block createTemplateLanguageBlock(ASTNode node, CodeStyleSettings settings, XmlFormattingPolicy xmlFormattingPolicy, Indent indent, @Nullable Alignment alignment, @Nullable Wrap wrap) { 35 | return new JteFormattingBlock(this, node, wrap, alignment, settings, xmlFormattingPolicy, indent, tokenTypes); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/org/jusecase/jte/intellij/language/parsing/LexerTest.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing; 2 | 3 | import com.intellij.psi.tree.IElementType; 4 | import org.junit.Assert; 5 | 6 | public abstract class LexerTest { 7 | private final Lexer lexer; 8 | 9 | protected LexerTest(Lexer lexer) { 10 | this.lexer = lexer; 11 | } 12 | 13 | protected void givenInput(String input) { 14 | lexer.start(input); 15 | } 16 | 17 | protected void thenTokensAre(Object... expectedTokenInfo) { 18 | StringBuilder expected = new StringBuilder(); 19 | for (Object tokenInfo : expectedTokenInfo) { 20 | if (tokenInfo instanceof IElementType) { 21 | appendTokenInfo(expected, (IElementType) tokenInfo); 22 | } else if (tokenInfo instanceof String) { 23 | appendTokenInfo(expected, (String) tokenInfo); 24 | } else { 25 | throw new IllegalArgumentException("Token info must be either IElementType or String."); 26 | } 27 | } 28 | 29 | StringBuilder actual = new StringBuilder(); 30 | while (lexer.getCurrentPosition().getOffset() < lexer.getBufferEnd()) { 31 | appendTokenInfo(actual, lexer.getTokenType(), lexer.getTokenText()); 32 | lexer.advance(); 33 | } 34 | 35 | Assert.assertEquals(expected.toString(), actual.toString()); 36 | } 37 | 38 | private void appendTokenInfo(StringBuilder result, IElementType tokenType, String tokenText) { 39 | appendTokenInfo(result, tokenType); 40 | appendTokenInfo(result, tokenText); 41 | } 42 | 43 | private void appendTokenInfo(StringBuilder result, IElementType tokenType) { 44 | result.append(tokenType).append(": "); 45 | } 46 | 47 | private void appendTokenInfo(StringBuilder result, String tokenText) { 48 | result.append(tokenText).append('\n'); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/org/jusecase/jte/intellij/language/KteKotlinContentManipulatorTest.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | import org.jusecase.jte.intellij.language.KteKotlinContentManipulator.ImportReplacement; 6 | 7 | public class KteKotlinContentManipulatorTest { 8 | 9 | @Test 10 | public void testBrokenImport() { 11 | ImportReplacement importReplacement = KteKotlinContentManipulator.isRequiredToOptimizeImports( 12 | "@import java.lang.Packageimport java.util.*\n" + 13 | "\n" + 14 | "@param x:String = \"foo\"\n" + 15 | "@param foo2:test.Model"); 16 | 17 | Assert.assertNotNull(importReplacement); 18 | Assert.assertEquals(importReplacement.oldText, "@import java.lang.Packageimport java.util.*\n"); 19 | Assert.assertEquals(importReplacement.newText, "@import java.lang.Package\n@import java.util.*\n"); 20 | } 21 | 22 | @Test 23 | public void testValidImport() { 24 | ImportReplacement importReplacement = KteKotlinContentManipulator.isRequiredToOptimizeImports( 25 | "@import java.lang.Package\n" + 26 | "@import java.util.*\n" + 27 | "@param x:String = \"foo\"\n" + 28 | "@param foo2:test.Model"); 29 | 30 | Assert.assertNull(importReplacement); 31 | } 32 | 33 | @Test 34 | public void name() { 35 | String text = "@import org.example.AnotherDummyimport org.example.Dummy\n" + 36 | "\n" + 37 | "@param foo: String?\n" + 38 | "@param bar: Dummy\n" + 39 | "@param x: AnotherDummy\n" + 40 | "\n" + 41 | "@if(foo?.isBlank())\n" + 42 | " Hello\n" + 43 | "@else\n" + 44 | " ${foo}\n" + 45 | "@endif\n" + 46 | "\n" + 47 | "\n"; 48 | } 49 | } -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/completion/JteTypedActionHandler.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.completion; 2 | 3 | import com.intellij.codeInsight.AutoPopupController; 4 | import com.intellij.codeInsight.editorActions.TypedHandlerDelegate; 5 | import com.intellij.openapi.editor.Editor; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.openapi.util.text.StringUtil; 8 | import com.intellij.psi.PsiDocumentManager; 9 | import com.intellij.psi.PsiElement; 10 | import com.intellij.psi.PsiFile; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jusecase.jte.intellij.language.parsing.JteTokenTypes; 13 | import org.jusecase.jte.intellij.language.parsing.KteTokenTypes; 14 | 15 | public class JteTypedActionHandler extends TypedHandlerDelegate { 16 | 17 | @Override 18 | public @NotNull Result charTyped(char charTyped, @NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) { 19 | if (charTyped == '@' || charTyped == '$' || charTyped == '!') { 20 | PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()); 21 | if (psiFile == null) { 22 | return Result.CONTINUE; 23 | } 24 | 25 | if (psiFile.getFileElementType() != JteTokenTypes.FILE && psiFile.getFileElementType() != KteTokenTypes.FILE) { 26 | return Result.CONTINUE; 27 | } 28 | 29 | invokeAutoPopup(project, editor, charTyped); 30 | return Result.STOP; 31 | } 32 | 33 | return Result.CONTINUE; 34 | } 35 | 36 | private void invokeAutoPopup(@NotNull Project project, @NotNull Editor editor, char charTyped) { 37 | AutoPopupController.getInstance(project).autoPopupMemberLookup(editor, file -> { 38 | int offset = editor.getCaretModel().getOffset(); 39 | 40 | PsiElement lastElement = file.findElementAt(offset - 1); 41 | return lastElement != null && StringUtil.endsWithChar(lastElement.getText(), charTyped); 42 | }); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/FoldingBuilderBase.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.intellij.lang.folding.CustomFoldingBuilder; 5 | import com.intellij.lang.folding.FoldingDescriptor; 6 | import com.intellij.openapi.editor.Document; 7 | import com.intellij.openapi.progress.ProgressManager; 8 | import com.intellij.openapi.util.TextRange; 9 | import com.intellij.psi.PsiElement; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jusecase.jte.intellij.language.psi.*; 12 | 13 | import java.util.List; 14 | 15 | public class FoldingBuilderBase extends CustomFoldingBuilder { 16 | 17 | @Override 18 | protected void buildLanguageFoldRegions(@NotNull List descriptors, @NotNull PsiElement root, @NotNull Document document, boolean quick) { 19 | addFoldRegions(descriptors, root); 20 | } 21 | 22 | private void addFoldRegions(@NotNull List descriptors, @NotNull PsiElement element) { 23 | final PsiElement[] children = element.getChildren(); 24 | 25 | for (PsiElement child : children) { 26 | ProgressManager.checkCanceled(); 27 | 28 | if (child instanceof JtePsiIf) { 29 | descriptors.add(new FoldingDescriptor(child.getNode(), child.getTextRange(), null, "@if(...)")); 30 | } else if (child instanceof JtePsiFor) { 31 | descriptors.add(new FoldingDescriptor(child.getNode(), child.getTextRange(), null, "@for(...)")); 32 | } else if (child instanceof JtePsiContent) { 33 | descriptors.add(new FoldingDescriptor(child.getNode(), child.getTextRange(), null, "@`...`")); 34 | } 35 | 36 | addFoldRegions(descriptors, child); 37 | } 38 | } 39 | 40 | @Override 41 | protected String getLanguagePlaceholderText(@NotNull ASTNode node, @NotNull TextRange range) { 42 | return null; 43 | } 44 | 45 | @Override 46 | protected boolean isRegionCollapsedByDefault(@NotNull ASTNode node) { 47 | return false; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/completion/JteHtmlCompletionConfidence.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.completion; 2 | 3 | import com.intellij.codeInsight.completion.CompletionConfidence; 4 | import com.intellij.lang.ASTNode; 5 | import com.intellij.openapi.util.text.StringUtil; 6 | import com.intellij.psi.PsiElement; 7 | import com.intellij.psi.PsiFile; 8 | import com.intellij.psi.tree.IFileElementType; 9 | import com.intellij.psi.xml.XmlDocument; 10 | import com.intellij.psi.xml.XmlText; 11 | import com.intellij.psi.xml.XmlTokenType; 12 | import com.intellij.util.ThreeState; 13 | import org.jetbrains.annotations.NotNull; 14 | import org.jusecase.jte.intellij.language.parsing.JteTokenTypes; 15 | import org.jusecase.jte.intellij.language.parsing.KteTokenTypes; 16 | 17 | /** 18 | * Workaround to prevent HTML confidence from suppressing auto popup 19 | */ 20 | public class JteHtmlCompletionConfidence extends CompletionConfidence { 21 | @NotNull 22 | @Override 23 | public ThreeState shouldSkipAutopopup(@NotNull PsiElement contextElement, @NotNull PsiFile psiFile, int offset) { 24 | IFileElementType fileElementType = psiFile.getFileElementType(); 25 | if (fileElementType != JteTokenTypes.FILE && fileElementType != KteTokenTypes.FILE) { 26 | return ThreeState.UNSURE; 27 | } 28 | 29 | ASTNode node = contextElement.getNode(); 30 | if (node != null && node.getElementType() == XmlTokenType.XML_DATA_CHARACTERS) { 31 | PsiElement parent = contextElement.getParent(); 32 | if (parent instanceof XmlText || parent instanceof XmlDocument) { 33 | String contextElementText = contextElement.getText(); 34 | int endOffset = offset - contextElement.getTextRange().getStartOffset(); 35 | String prefix = contextElementText.substring(0, Math.min(contextElementText.length(), endOffset)); 36 | if (StringUtil.startsWithChar(prefix, '@') || StringUtil.startsWithChar(prefix, '$') || StringUtil.startsWithChar(prefix, '!')) { 37 | return ThreeState.NO; 38 | } 39 | } 40 | } 41 | 42 | return ThreeState.UNSURE; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/format/KteCodeStyleSettingsProvider.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.format; 2 | 3 | import com.intellij.application.options.CodeStyleAbstractConfigurable; 4 | import com.intellij.application.options.CodeStyleAbstractPanel; 5 | import com.intellij.application.options.TabbedLanguageCodeStylePanel; 6 | import com.intellij.lang.Language; 7 | import com.intellij.psi.codeStyle.CodeStyleConfigurable; 8 | import com.intellij.psi.codeStyle.CodeStyleSettings; 9 | import com.intellij.psi.codeStyle.CodeStyleSettingsProvider; 10 | import com.intellij.psi.codeStyle.CustomCodeStyleSettings; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | import org.jusecase.jte.intellij.language.KteLanguage; 14 | 15 | @SuppressWarnings("DialogTitleCapitalization") 16 | public class KteCodeStyleSettingsProvider extends CodeStyleSettingsProvider { 17 | 18 | @Override 19 | @Nullable 20 | public String getConfigurableDisplayName() { 21 | return KteLanguage.INSTANCE.getDisplayName(); 22 | } 23 | 24 | @Override 25 | public @Nullable Language getLanguage() { 26 | return KteLanguage.INSTANCE; 27 | } 28 | 29 | @Override 30 | public @Nullable CustomCodeStyleSettings createCustomSettings(CodeStyleSettings settings) { 31 | return new KteCodeStyleSettings(settings); 32 | } 33 | 34 | @Override 35 | public @NotNull CodeStyleConfigurable createConfigurable(@NotNull CodeStyleSettings settings, @NotNull CodeStyleSettings modelSettings) { 36 | return new CodeStyleAbstractConfigurable(settings, modelSettings, getConfigurableDisplayName()) { 37 | @Override 38 | protected CodeStyleAbstractPanel createPanel(CodeStyleSettings settings) { 39 | return new KteCodeStyleMainPanel(getCurrentSettings(), settings); 40 | } 41 | }; 42 | } 43 | 44 | private static class KteCodeStyleMainPanel extends TabbedLanguageCodeStylePanel { 45 | 46 | public KteCodeStyleMainPanel(CodeStyleSettings currentSettings, CodeStyleSettings settings) { 47 | super(KteLanguage.INSTANCE, currentSettings, settings); 48 | } 49 | 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/TemplateHighlighter.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language; 2 | 3 | import com.intellij.ide.highlighter.HtmlFileType; 4 | import com.intellij.lang.Language; 5 | import com.intellij.openapi.editor.colors.EditorColorsScheme; 6 | import com.intellij.openapi.editor.ex.util.LayerDescriptor; 7 | import com.intellij.openapi.editor.ex.util.LayeredLexerEditorHighlighter; 8 | import com.intellij.openapi.fileTypes.*; 9 | import com.intellij.openapi.project.Project; 10 | import com.intellij.openapi.vfs.VirtualFile; 11 | import com.intellij.psi.templateLanguages.TemplateDataLanguageMappings; 12 | import org.jetbrains.annotations.NotNull; 13 | import org.jetbrains.annotations.Nullable; 14 | import org.jusecase.jte.intellij.language.parsing.TokenTypes; 15 | 16 | public class TemplateHighlighter extends LayeredLexerEditorHighlighter { 17 | public TemplateHighlighter(@Nullable Project project, @Nullable VirtualFile virtualFile, @NotNull EditorColorsScheme colors, LanguageFileType expressionLanguageFileType, TokenTypes tokenTypes, SyntaxHighlighter syntaxHighlighter) { 18 | super(syntaxHighlighter, colors); 19 | 20 | FileType type = null; 21 | if (project == null || virtualFile == null) { 22 | type = FileTypes.PLAIN_TEXT; 23 | } 24 | else { 25 | Language language = TemplateDataLanguageMappings.getInstance(project).getMapping(virtualFile); 26 | if (language != null) type = language.getAssociatedFileType(); 27 | if (type == null) type = HtmlFileType.INSTANCE; 28 | } 29 | 30 | SyntaxHighlighter outerHighlighter = SyntaxHighlighterFactory.getSyntaxHighlighter(type, project, virtualFile); 31 | if (outerHighlighter != null) { 32 | registerLayer(tokenTypes.HTML_CONTENT(), new LayerDescriptor(outerHighlighter, "")); 33 | } 34 | 35 | SyntaxHighlighter outerExpressionLanguageHighlighter = SyntaxHighlighterFactory.getSyntaxHighlighter(expressionLanguageFileType, project, virtualFile); 36 | if (outerExpressionLanguageHighlighter != null) { 37 | registerLayer(tokenTypes.JAVA_INJECTION(), new LayerDescriptor(outerExpressionLanguageHighlighter, "")); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/highlighting/JsExpressionErrorFilter.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.highlighting; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | import org.jusecase.jte.intellij.language.JteLanguage; 5 | import org.jusecase.jte.intellij.language.KteLanguage; 6 | 7 | import com.intellij.codeInsight.highlighting.HighlightErrorFilter; 8 | import com.intellij.lang.Language; 9 | import com.intellij.psi.PsiElement; 10 | import com.intellij.psi.PsiErrorElement; 11 | import com.intellij.psi.PsiFile; 12 | import com.intellij.psi.PsiWhiteSpace; 13 | import com.intellij.psi.templateLanguages.OuterLanguageElement; 14 | 15 | 16 | final class JsExpressionErrorFilter extends HighlightErrorFilter { 17 | 18 | @Override 19 | public boolean shouldHighlightErrorElement( @NotNull final PsiErrorElement element ) { 20 | Language elementBaseLanguage = element.getLanguage().getBaseLanguage(); 21 | if ( elementBaseLanguage == null ) { 22 | return true; 23 | } 24 | 25 | if ( !"JavaScript".equals(elementBaseLanguage.getID()) ) { 26 | return true; 27 | } 28 | 29 | final PsiFile psiFile = element.getContainingFile(); 30 | if ( psiFile == null ) { 31 | return true; 32 | } 33 | 34 | Language baseLanguage = psiFile.getViewProvider().getBaseLanguage(); 35 | if ( baseLanguage != JteLanguage.INSTANCE && baseLanguage != KteLanguage.INSTANCE ) { 36 | return true; 37 | } 38 | 39 | if ( !"Expression expected".equals(element.getErrorDescription()) ) { 40 | return true; 41 | } 42 | 43 | for ( PsiElement e = element.getParent(); e != null; e = e.getNextSibling() ) { 44 | if ( e instanceof PsiWhiteSpace ) { 45 | continue; 46 | } 47 | 48 | if ( e instanceof OuterLanguageElement ) { 49 | String elementText = e.getText(); 50 | 51 | //noinspection RedundantIfStatement 52 | if ( elementText != null && (elementText.startsWith("${") || elementText.startsWith("$unsafe{")) ) { 53 | return false; 54 | } else { 55 | return true; 56 | } 57 | } 58 | } 59 | 60 | return true; 61 | } 62 | } 63 | 64 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/format/JteCodeStyleSettingsProvider.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.format; 2 | 3 | import com.intellij.application.options.CodeStyleAbstractConfigurable; 4 | import com.intellij.application.options.CodeStyleAbstractPanel; 5 | import com.intellij.application.options.TabbedLanguageCodeStylePanel; 6 | import com.intellij.lang.Language; 7 | import com.intellij.psi.codeStyle.CodeStyleConfigurable; 8 | import com.intellij.psi.codeStyle.CodeStyleSettings; 9 | import com.intellij.psi.codeStyle.CodeStyleSettingsProvider; 10 | import com.intellij.psi.codeStyle.CustomCodeStyleSettings; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | import org.jusecase.jte.intellij.language.JteLanguage; 14 | 15 | @SuppressWarnings("DialogTitleCapitalization") 16 | public class JteCodeStyleSettingsProvider extends CodeStyleSettingsProvider { 17 | 18 | @Override 19 | @Nullable 20 | public String getConfigurableDisplayName() { 21 | return JteLanguage.INSTANCE.getDisplayName(); 22 | } 23 | 24 | @Override 25 | @Nullable 26 | public Language getLanguage() { 27 | return JteLanguage.INSTANCE; 28 | } 29 | 30 | @Override 31 | @Nullable 32 | public CustomCodeStyleSettings createCustomSettings(@NotNull CodeStyleSettings settings) { 33 | return new JteCodeStyleSettings(settings); 34 | } 35 | 36 | @Override 37 | public @NotNull CodeStyleConfigurable createConfigurable(@NotNull CodeStyleSettings settings, @NotNull CodeStyleSettings modelSettings) { 38 | return new CodeStyleAbstractConfigurable(settings, modelSettings, getConfigurableDisplayName()) { 39 | @NotNull 40 | @Override 41 | protected CodeStyleAbstractPanel createPanel(@NotNull CodeStyleSettings settings) { 42 | return new JteCodeStyleMainPanel(getCurrentSettings(), settings); 43 | } 44 | }; 45 | } 46 | 47 | private static class JteCodeStyleMainPanel extends TabbedLanguageCodeStylePanel { 48 | 49 | public JteCodeStyleMainPanel(CodeStyleSettings currentSettings, CodeStyleSettings settings) { 50 | super(JteLanguage.INSTANCE, currentSettings, settings); 51 | } 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/HighlighterMapping.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language; 2 | 3 | import com.intellij.openapi.editor.DefaultLanguageHighlighterColors; 4 | import com.intellij.openapi.editor.colors.TextAttributesKey; 5 | import com.intellij.psi.tree.IElementType; 6 | import org.jusecase.jte.intellij.language.parsing.TokenTypes; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | public class HighlighterMapping { 12 | private static final TextAttributesKey[] KEYWORD = {TextAttributesKey.createTextAttributesKey( 13 | "JAVA_TEMPLATE_ENGINE.KEYWORD", 14 | DefaultLanguageHighlighterColors.KEYWORD 15 | )}; 16 | 17 | private static final TextAttributesKey[] COMMENT = {TextAttributesKey.createTextAttributesKey( 18 | "JAVA_TEMPLATE_ENGINE.COMMENT", 19 | DefaultLanguageHighlighterColors.BLOCK_COMMENT 20 | )}; 21 | 22 | private static final TextAttributesKey[] STRING = {TextAttributesKey.createTextAttributesKey( 23 | "JAVA_TEMPLATE_ENGINE.STRING", 24 | DefaultLanguageHighlighterColors.STRING 25 | )}; 26 | 27 | public static Map create(TokenTypes tokens) { 28 | Map mapping = new HashMap<>(); 29 | 30 | mapping.put(tokens.IMPORT(), KEYWORD); 31 | mapping.put(tokens.PARAM(), KEYWORD); 32 | mapping.put(tokens.IF(), KEYWORD); 33 | mapping.put(tokens.ELSE(), KEYWORD); 34 | mapping.put(tokens.ELSEIF(), KEYWORD); 35 | mapping.put(tokens.ENDIF(), KEYWORD); 36 | mapping.put(tokens.FOR(), KEYWORD); 37 | mapping.put(tokens.ENDFOR(), KEYWORD); 38 | mapping.put(tokens.RAW(), KEYWORD); 39 | mapping.put(tokens.ENDRAW(), KEYWORD); 40 | mapping.put(tokens.TEMPLATE(), KEYWORD); 41 | mapping.put(tokens.CONTENT_BEGIN(), STRING); 42 | mapping.put(tokens.CONTENT_END(), STRING); 43 | 44 | mapping.put(tokens.COMMENT(), COMMENT); 45 | 46 | mapping.put(tokens.OUTPUT_BEGIN(), KEYWORD); 47 | mapping.put(tokens.OUTPUT_END(), KEYWORD); 48 | mapping.put(tokens.STATEMENT_BEGIN(), KEYWORD); 49 | mapping.put(tokens.STATEMENT_END(), KEYWORD); 50 | 51 | return mapping; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiJavaContent.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.intellij.openapi.util.TextRange; 5 | import com.intellij.psi.ElementManipulators; 6 | import com.intellij.psi.LiteralTextEscaper; 7 | import com.intellij.psi.PsiLanguageInjectionHost; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | public class JtePsiJavaContent extends JtePsiElement implements PsiLanguageInjectionHost { 11 | public JtePsiJavaContent(@NotNull ASTNode node) { 12 | super(node); 13 | } 14 | 15 | @Override 16 | public boolean isValidHost() { 17 | return true; 18 | } 19 | 20 | @Override 21 | public PsiLanguageInjectionHost updateText(@NotNull String text) { 22 | return ElementManipulators.handleContentChange(this, text); 23 | } 24 | 25 | @NotNull 26 | @Override 27 | public LiteralTextEscaper createLiteralTextEscaper() { 28 | return new LiteralTextEscaper(this) { 29 | @Override 30 | public boolean decode(@NotNull TextRange rangeInsideHost, @NotNull StringBuilder outChars) { 31 | outChars.append(myHost.getText(), rangeInsideHost.getStartOffset(), rangeInsideHost.getEndOffset()); 32 | return true; 33 | } 34 | 35 | @Override 36 | public int getOffsetInHost(int offsetInDecoded, @NotNull TextRange rangeInsideHost) { 37 | // int offset = offsetInDecoded + rangeInsideHost.getStartOffset(); 38 | // if (offset < rangeInsideHost.getStartOffset()) offset = rangeInsideHost.getStartOffset(); 39 | // if (offset > rangeInsideHost.getEndOffset()) offset = rangeInsideHost.getEndOffset(); 40 | // return offset; 41 | 42 | int offset = offsetInDecoded + rangeInsideHost.getStartOffset(); 43 | 44 | if (offset < rangeInsideHost.getStartOffset()) { 45 | return -1; 46 | } 47 | 48 | if (offset > rangeInsideHost.getEndOffset()) { 49 | return -1; 50 | } 51 | 52 | return offset; 53 | } 54 | 55 | @Override 56 | public boolean isOneLine() { 57 | return false; 58 | } 59 | }; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/KtePsiJavaContent.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.intellij.openapi.util.TextRange; 5 | import com.intellij.psi.ElementManipulators; 6 | import com.intellij.psi.LiteralTextEscaper; 7 | import com.intellij.psi.PsiLanguageInjectionHost; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | public class KtePsiJavaContent extends JtePsiElement implements PsiLanguageInjectionHost { 11 | public KtePsiJavaContent(@NotNull ASTNode node) { 12 | super(node); 13 | } 14 | 15 | @Override 16 | public boolean isValidHost() { 17 | return true; 18 | } 19 | 20 | @Override 21 | public PsiLanguageInjectionHost updateText(@NotNull String text) { 22 | return ElementManipulators.handleContentChange(this, text); 23 | } 24 | 25 | @NotNull 26 | @Override 27 | public LiteralTextEscaper createLiteralTextEscaper() { 28 | return new LiteralTextEscaper(this) { 29 | @Override 30 | public boolean decode(@NotNull TextRange rangeInsideHost, @NotNull StringBuilder outChars) { 31 | outChars.append(myHost.getText(), rangeInsideHost.getStartOffset(), rangeInsideHost.getEndOffset()); 32 | return true; 33 | } 34 | 35 | @Override 36 | public int getOffsetInHost(int offsetInDecoded, @NotNull TextRange rangeInsideHost) { 37 | // int offset = offsetInDecoded + rangeInsideHost.getStartOffset(); 38 | // if (offset < rangeInsideHost.getStartOffset()) offset = rangeInsideHost.getStartOffset(); 39 | // if (offset > rangeInsideHost.getEndOffset()) offset = rangeInsideHost.getEndOffset(); 40 | // return offset; 41 | 42 | int offset = offsetInDecoded + rangeInsideHost.getStartOffset(); 43 | 44 | if (offset < rangeInsideHost.getStartOffset()) { 45 | return -1; 46 | } 47 | 48 | if (offset > rangeInsideHost.getEndOffset()) { 49 | return -1; 50 | } 51 | 52 | return offset; 53 | } 54 | 55 | @Override 56 | public boolean isOneLine() { 57 | return false; 58 | } 59 | }; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiExtraJavaInjection.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.intellij.openapi.util.TextRange; 5 | import com.intellij.psi.LiteralTextEscaper; 6 | import com.intellij.psi.PsiLanguageInjectionHost; 7 | import com.intellij.psi.impl.source.tree.ChangeUtil; 8 | import com.intellij.psi.impl.source.tree.LeafElement; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | public class JtePsiExtraJavaInjection extends JtePsiElement implements PsiLanguageInjectionHost { 12 | public JtePsiExtraJavaInjection(@NotNull ASTNode node) { 13 | super(node); 14 | } 15 | 16 | @Override 17 | public boolean isValidHost() { 18 | return true; 19 | } 20 | 21 | @Override 22 | public PsiLanguageInjectionHost updateText(@NotNull String text) { 23 | ASTNode firstChildNode = getNode().getFirstChildNode(); 24 | if (firstChildNode == null) { 25 | return this; 26 | } 27 | 28 | if (!(firstChildNode instanceof LeafElement)) { 29 | return this; 30 | } 31 | 32 | LeafElement oldLeaf = (LeafElement) firstChildNode; 33 | LeafElement newLeaf = ChangeUtil.copyLeafWithText(oldLeaf, text); 34 | 35 | oldLeaf.getTreeParent().replaceChild(oldLeaf, newLeaf); 36 | 37 | return this; 38 | } 39 | 40 | @NotNull 41 | @Override 42 | public LiteralTextEscaper createLiteralTextEscaper() { 43 | return new LiteralTextEscaper(this) { 44 | @Override 45 | public boolean decode(@NotNull TextRange rangeInsideHost, @NotNull StringBuilder outChars) { 46 | outChars.append(myHost.getText(), rangeInsideHost.getStartOffset(), rangeInsideHost.getEndOffset()); 47 | return true; 48 | } 49 | 50 | @Override 51 | public int getOffsetInHost(int offsetInDecoded, @NotNull TextRange rangeInsideHost) { 52 | int offset = offsetInDecoded + rangeInsideHost.getStartOffset(); 53 | 54 | if (offset < rangeInsideHost.getStartOffset()) { 55 | return -1; 56 | } 57 | 58 | if (offset > rangeInsideHost.getEndOffset()) { 59 | return -1; 60 | } 61 | 62 | return offset; 63 | } 64 | 65 | @Override 66 | public boolean isOneLine() { 67 | return true; 68 | } 69 | }; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/JteJavaContentManipulator.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language; 2 | 3 | import com.intellij.openapi.application.ApplicationManager; 4 | import com.intellij.openapi.command.WriteCommandAction; 5 | import com.intellij.openapi.editor.Document; 6 | import com.intellij.openapi.fileEditor.FileDocumentManager; 7 | import com.intellij.openapi.util.TextRange; 8 | import com.intellij.openapi.vfs.VirtualFile; 9 | import com.intellij.psi.AbstractElementManipulator; 10 | import com.intellij.psi.PsiDocumentManager; 11 | import com.intellij.util.IncorrectOperationException; 12 | import org.jetbrains.annotations.NotNull; 13 | import org.jetbrains.annotations.Nullable; 14 | import org.jusecase.jte.intellij.language.psi.JtePsiJavaContent; 15 | 16 | public class JteJavaContentManipulator extends AbstractElementManipulator { 17 | @Nullable 18 | @Override 19 | public JtePsiJavaContent handleContentChange(@NotNull JtePsiJavaContent element, @NotNull TextRange range, String newContent) throws IncorrectOperationException { 20 | 21 | VirtualFile virtualFile = element.getContainingFile().getVirtualFile(); 22 | if (virtualFile == null) { 23 | return element; 24 | } 25 | 26 | Document document = FileDocumentManager.getInstance().getDocument(virtualFile); 27 | if (document != null) { 28 | PsiDocumentManager documentManager = PsiDocumentManager.getInstance(element.getProject()); 29 | if (documentManager.isDocumentBlockedByPsi(document)) { 30 | documentManager.doPostponedOperationsAndUnblockDocument(document); 31 | } 32 | 33 | document.replaceString(range.getStartOffset(), range.getEndOffset(), newContent); 34 | documentManager.commitDocument(document); 35 | 36 | optimizeImportsAfterContentManipulation(document, element, newContent); 37 | } 38 | 39 | return element; 40 | } 41 | 42 | private void optimizeImportsAfterContentManipulation(@NotNull Document document, @NotNull JtePsiJavaContent element, String newContent) { 43 | if (isRequiredToOptimizeImports(newContent)) { 44 | ApplicationManager.getApplication().invokeLater(() -> { 45 | //noinspection DialogTitleCapitalization 46 | WriteCommandAction.runWriteCommandAction(element.getProject(), "Optimize jte Imports", null, () -> { 47 | String optimizedText = document.getText().replace(";import ", "\n@import "); 48 | document.setText(optimizedText); 49 | }, element.getContainingFile()); 50 | }); 51 | } 52 | } 53 | 54 | private boolean isRequiredToOptimizeImports(String newContent) { 55 | return newContent.contains(";import "); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/parsers/AbstractTokenParser.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing.parsers; 2 | 3 | import com.intellij.ide.highlighter.custom.tokens.TokenParser; 4 | import com.intellij.psi.tree.IElementType; 5 | import org.jusecase.jte.intellij.language.parsing.Lexer; 6 | 7 | public abstract class AbstractTokenParser extends TokenParser { 8 | 9 | protected final Lexer lexer; 10 | 11 | public AbstractTokenParser(Lexer lexer) { 12 | this.lexer = lexer; 13 | } 14 | 15 | protected final boolean isBeginOf(int position, char token) { 16 | if (position >= myEndOffset) { 17 | return false; 18 | } 19 | return myBuffer.charAt(position) == token; 20 | } 21 | 22 | protected final boolean isBeginOf(int position, String token) { 23 | int endPosition = position + token.length(); 24 | if (endPosition > myEndOffset) { 25 | return false; 26 | } 27 | 28 | for (int i = position; i < endPosition; ++i) { 29 | if (myBuffer.charAt(i) != token.charAt(i - position)) { 30 | return false; 31 | } 32 | } 33 | 34 | return true; 35 | } 36 | 37 | @SuppressWarnings("SameParameterValue") 38 | protected final boolean isEndOf(int position, String token) { 39 | int startPosition = position - token.length(); 40 | if (startPosition < myStartOffset) { 41 | return false; 42 | } 43 | 44 | for (int i = startPosition; i < position; ++i) { 45 | if (myBuffer.charAt(i) != token.charAt(i - startPosition)) { 46 | return false; 47 | } 48 | } 49 | 50 | return true; 51 | } 52 | 53 | protected final boolean hasToken(int position, String keyword, IElementType type) { 54 | if (isBeginOf(position, keyword)) { 55 | myTokenInfo.updateData(position, position + keyword.length(), type); 56 | return true; 57 | } 58 | return false; 59 | } 60 | 61 | protected final boolean skipWhitespaces(int position) { 62 | if (!isWhitespace(position)) return false; 63 | int start = position; 64 | //noinspection StatementWithEmptyBody 65 | for (position++; position < myEndOffset && isWhitespace(position); position++) ; 66 | myTokenInfo.updateData(start, position, lexer.tokens.WHITESPACE()); 67 | return true; 68 | } 69 | 70 | protected boolean isWhitespace(int position) { 71 | return Character.isWhitespace(myBuffer.charAt(position)); 72 | } 73 | 74 | protected boolean isBlank(int position, int end) { 75 | for (int i = position; i < end; ++i) { 76 | if (!isWhitespace(i)) { 77 | return false; 78 | } 79 | } 80 | return true; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/format/JteFormattingBlock.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.format; 2 | 3 | import com.intellij.formatting.*; 4 | import com.intellij.lang.ASTNode; 5 | import com.intellij.psi.codeStyle.CodeStyleSettings; 6 | import com.intellij.psi.formatter.common.AbstractBlock; 7 | import com.intellij.psi.formatter.xml.XmlFormattingPolicy; 8 | import com.intellij.psi.tree.IElementType; 9 | import com.intellij.xml.template.formatter.AbstractXmlTemplateFormattingModelBuilder; 10 | import com.intellij.xml.template.formatter.TemplateLanguageBlock; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | import org.jusecase.jte.intellij.language.parsing.TokenTypes; 14 | 15 | import java.util.List; 16 | 17 | public class JteFormattingBlock extends TemplateLanguageBlock { 18 | private final TokenTypes tokenTypes; 19 | 20 | protected JteFormattingBlock(AbstractXmlTemplateFormattingModelBuilder builder, @NotNull ASTNode node, @Nullable Wrap wrap, @Nullable Alignment alignment, CodeStyleSettings settings, XmlFormattingPolicy xmlFormattingPolicy, @Nullable Indent indent, TokenTypes tokenTypes) { 21 | super(builder, node, wrap, alignment, settings, xmlFormattingPolicy, indent); 22 | this.tokenTypes = tokenTypes; 23 | } 24 | 25 | @Override 26 | protected @NotNull Indent getChildIndent(@NotNull ASTNode node) { 27 | if (myNode.getElementType() == tokenTypes.BLOCK()) { 28 | return Indent.getNormalIndent(); 29 | } 30 | return Indent.getNoneIndent(); 31 | } 32 | 33 | @Override 34 | protected Spacing getSpacing(TemplateLanguageBlock adjacentBlock) { 35 | return null; 36 | } 37 | 38 | @Override 39 | public @Nullable Spacing getSpacing(@Nullable Block child1, @NotNull Block child2) { 40 | return null; 41 | } 42 | 43 | @Override 44 | public @NotNull ChildAttributes getChildAttributes(int newChildIndex) { 45 | AbstractBlock previousBlock = getPreviousBlock(newChildIndex); 46 | 47 | if (previousBlock != null) { 48 | IElementType elementType = previousBlock.getNode().getElementType(); 49 | if (elementType == tokenTypes.IMPORT() || elementType == tokenTypes.PARAM()) { 50 | return new ChildAttributes(Indent.getNoneIndent(), null); 51 | } 52 | } 53 | 54 | return super.getChildAttributes(newChildIndex); 55 | } 56 | 57 | private AbstractBlock getPreviousBlock(int newChildIndex) { 58 | List subBlocks = getSubBlocks(); 59 | if (newChildIndex > subBlocks.size()) { 60 | return null; 61 | } 62 | 63 | Block previousBlock = subBlocks.get(newChildIndex - 1); 64 | if (!(previousBlock instanceof AbstractBlock)) { 65 | return null; 66 | } 67 | 68 | return (AbstractBlock)previousBlock; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/resources/liveTemplates/jte.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 17 | 18 | 22 | 23 | 26 | 27 | 31 | 32 | 36 | 37 | 41 | 42 | 46 | 47 | 50 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/KteKotlinContentManipulator.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language; 2 | 3 | import com.intellij.openapi.application.ApplicationManager; 4 | import com.intellij.openapi.command.WriteCommandAction; 5 | import com.intellij.openapi.editor.Document; 6 | import com.intellij.openapi.fileEditor.FileDocumentManager; 7 | import com.intellij.openapi.util.TextRange; 8 | import com.intellij.openapi.vfs.VirtualFile; 9 | import com.intellij.psi.AbstractElementManipulator; 10 | import com.intellij.psi.PsiDocumentManager; 11 | import com.intellij.util.IncorrectOperationException; 12 | import org.jetbrains.annotations.NotNull; 13 | import org.jetbrains.annotations.Nullable; 14 | import org.jusecase.jte.intellij.language.psi.KtePsiJavaContent; 15 | 16 | import java.util.regex.Matcher; 17 | import java.util.regex.Pattern; 18 | 19 | public class KteKotlinContentManipulator extends AbstractElementManipulator { 20 | 21 | private static final Pattern BROKEN_IMPORT = Pattern.compile("(@import\\s*\\S*)(import\\s*\\S*\\n)"); 22 | 23 | @Nullable 24 | @Override 25 | public KtePsiJavaContent handleContentChange(@NotNull KtePsiJavaContent element, @NotNull TextRange range, String newContent) throws IncorrectOperationException { 26 | 27 | VirtualFile virtualFile = element.getContainingFile().getVirtualFile(); 28 | if (virtualFile == null) { 29 | return element; 30 | } 31 | 32 | Document document = FileDocumentManager.getInstance().getDocument(virtualFile); 33 | if (document != null) { 34 | PsiDocumentManager documentManager = PsiDocumentManager.getInstance(element.getProject()); 35 | if (documentManager.isDocumentBlockedByPsi(document)) { 36 | documentManager.doPostponedOperationsAndUnblockDocument(document); 37 | } 38 | 39 | document.replaceString(range.getStartOffset(), range.getEndOffset(), newContent); 40 | documentManager.commitDocument(document); 41 | 42 | optimizeImportsAfterContentManipulation(document, element, newContent); 43 | } 44 | 45 | return element; 46 | } 47 | 48 | private void optimizeImportsAfterContentManipulation(@NotNull Document document, @NotNull KtePsiJavaContent element, String newContent) { 49 | ImportReplacement importReplacement = isRequiredToOptimizeImports(newContent); 50 | 51 | if (importReplacement != null) { 52 | ApplicationManager.getApplication().invokeLater(() -> { 53 | //noinspection DialogTitleCapitalization 54 | WriteCommandAction.runWriteCommandAction(element.getProject(), "Optimize kte Imports", null, () -> { 55 | String optimizedText = document.getText().replace(importReplacement.oldText, importReplacement.newText); 56 | document.setText(optimizedText); 57 | }, element.getContainingFile()); 58 | }); 59 | } 60 | } 61 | 62 | static ImportReplacement isRequiredToOptimizeImports(String newContent) { 63 | Matcher matcher = BROKEN_IMPORT.matcher(newContent); 64 | if (matcher.find()) { 65 | String oldText = matcher.group(); 66 | String import1 = matcher.group(1); 67 | String import2 = matcher.group(2); 68 | 69 | return new ImportReplacement(oldText, import1 + "\n@" + import2); 70 | } 71 | 72 | return null; 73 | } 74 | 75 | static class ImportReplacement { 76 | final String oldText; 77 | final String newText; 78 | 79 | public ImportReplacement(String oldText, String newText) { 80 | this.oldText = oldText; 81 | this.newText = newText; 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /src/main/resources/META-INF/pluginIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/KtePsiParamName.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.intellij.openapi.util.TextRange; 5 | import com.intellij.psi.*; 6 | import com.intellij.psi.impl.SharedPsiElementImplUtil; 7 | import com.intellij.psi.util.PsiTreeUtil; 8 | import com.intellij.util.IncorrectOperationException; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | import org.jetbrains.kotlin.psi.KtParameter; 12 | import org.jetbrains.kotlin.psi.KtParameterList; 13 | 14 | public class KtePsiParamName extends JtePsiElement { 15 | public KtePsiParamName(@NotNull ASTNode node) { 16 | super(node); 17 | } 18 | 19 | @Override 20 | public PsiReference getReference() { 21 | String name = getName(); 22 | if (name == null) { 23 | return null; 24 | } 25 | 26 | JtePsiTemplateName templateName = PsiTreeUtil.getPrevSiblingOfType(this, JtePsiTemplateName.class); 27 | if (templateName == null) { 28 | return null; 29 | } 30 | 31 | PsiFile templateFile = templateName.resolveFile(); 32 | if (templateFile == null) { 33 | return null; 34 | } 35 | 36 | KtParameterList parameterList = KtePsiUtil.resolveParameterList(templateFile); 37 | if (parameterList == null) { 38 | return null; 39 | } 40 | 41 | KtParameter parameter = getParameterWithSameName(name, parameterList); 42 | if (parameter == null) { 43 | return null; 44 | } 45 | 46 | return createReferenceFor(parameter); 47 | } 48 | 49 | @NotNull 50 | @Override 51 | public PsiReference[] getReferences() { 52 | return SharedPsiElementImplUtil.getReferences(this); 53 | } 54 | 55 | @Override 56 | public String getName() { 57 | return getText(); 58 | } 59 | 60 | private KtParameter getParameterWithSameName(String name, KtParameterList parameterList) { 61 | for (KtParameter parameter : parameterList.getParameters()) { 62 | if (name.equals(parameter.getName())) { 63 | return parameter; 64 | } 65 | } 66 | return null; 67 | } 68 | 69 | private PsiReference createReferenceFor(KtParameter parameter) { 70 | return new PsiReference() { 71 | @NotNull 72 | @Override 73 | public PsiElement getElement() { 74 | return KtePsiParamName.this; 75 | } 76 | 77 | @NotNull 78 | @Override 79 | public TextRange getRangeInElement() { 80 | return TextRange.from(0, getTextLength()); 81 | } 82 | 83 | @Nullable 84 | @Override 85 | public PsiElement resolve() { 86 | return parameter; 87 | } 88 | 89 | @NotNull 90 | @Override 91 | public String getCanonicalText() { 92 | return getText(); 93 | } 94 | 95 | @Override 96 | public PsiElement handleElementRename(@NotNull String newElementName) throws IncorrectOperationException { 97 | JtePsiUtil.rename(KtePsiParamName.this, newElementName); 98 | return KtePsiParamName.this; 99 | } 100 | 101 | @Override 102 | public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException { 103 | return null; // TODO ??? 104 | } 105 | 106 | @Override 107 | public boolean isReferenceTo(@NotNull PsiElement element) { 108 | return element == parameter; 109 | } 110 | 111 | @Override 112 | public boolean isSoft() { 113 | return false; 114 | } 115 | }; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiParamName.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.intellij.openapi.util.TextRange; 5 | import com.intellij.psi.*; 6 | import com.intellij.psi.impl.SharedPsiElementImplUtil; 7 | import com.intellij.psi.util.PsiTreeUtil; 8 | import com.intellij.util.IncorrectOperationException; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | public class JtePsiParamName extends JtePsiElement { 13 | public JtePsiParamName(@NotNull ASTNode node) { 14 | super(node); 15 | } 16 | 17 | @Override 18 | public PsiReference getReference() { 19 | String name = getName(); 20 | if (name == null) { 21 | return null; 22 | } 23 | 24 | JtePsiTemplateName templateName = PsiTreeUtil.getPrevSiblingOfType(this, JtePsiTemplateName.class); 25 | if (templateName == null) { 26 | return null; 27 | } 28 | 29 | PsiFile templateFile = templateName.resolveFile(); 30 | if (templateFile == null) { 31 | return null; 32 | } 33 | 34 | PsiParameterList parameterList = JtePsiUtil.resolveParameterList(templateFile); 35 | if (parameterList == null) { 36 | return null; 37 | } 38 | 39 | PsiParameter parameter = getParameterWithSameName(name, parameterList); 40 | if (parameter == null) { 41 | return null; 42 | } 43 | 44 | return createReferenceFor(parameter); 45 | } 46 | 47 | @NotNull 48 | @Override 49 | public PsiReference[] getReferences() { 50 | return SharedPsiElementImplUtil.getReferences(this); 51 | } 52 | 53 | @Override 54 | public String getName() { 55 | return getText(); 56 | } 57 | 58 | public PsiParameter getParameterWithSameName(PsiParameterList parameterList) { 59 | String name = getName(); 60 | if (name == null) { 61 | return null; 62 | } 63 | return getParameterWithSameName(name, parameterList); 64 | } 65 | 66 | private PsiParameter getParameterWithSameName(String name, PsiParameterList parameterList) { 67 | for (PsiParameter parameter : parameterList.getParameters()) { 68 | if (name.equals(parameter.getName())) { 69 | return parameter; //JtePsiUtil.getLastChildOfType(parameter, PsiIdentifier.class); 70 | } 71 | } 72 | return null; 73 | } 74 | 75 | private PsiReference createReferenceFor(PsiParameter parameter) { 76 | return new PsiReference() { 77 | @NotNull 78 | @Override 79 | public PsiElement getElement() { 80 | return JtePsiParamName.this; 81 | } 82 | 83 | @NotNull 84 | @Override 85 | public TextRange getRangeInElement() { 86 | return TextRange.from(0, getTextLength()); 87 | } 88 | 89 | @Nullable 90 | @Override 91 | public PsiElement resolve() { 92 | return parameter; 93 | } 94 | 95 | @NotNull 96 | @Override 97 | public String getCanonicalText() { 98 | return getText(); 99 | } 100 | 101 | @Override 102 | public PsiElement handleElementRename(@NotNull String newElementName) throws IncorrectOperationException { 103 | JtePsiUtil.rename(JtePsiParamName.this, newElementName); 104 | return JtePsiParamName.this; 105 | } 106 | 107 | @Override 108 | public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException { 109 | return null; // TODO ??? 110 | } 111 | 112 | @Override 113 | public boolean isReferenceTo(@NotNull PsiElement element) { 114 | return element == parameter; 115 | } 116 | 117 | @Override 118 | public boolean isSoft() { 119 | return false; 120 | } 121 | }; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/completion/AbstractTemplateCompletionProvider.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.completion; 2 | 3 | import com.intellij.codeInsight.completion.*; 4 | import com.intellij.codeInsight.lookup.LookupElement; 5 | import com.intellij.codeInsight.lookup.LookupElementBuilder; 6 | import com.intellij.icons.AllIcons; 7 | import com.intellij.psi.*; 8 | import com.intellij.psi.util.PsiTreeUtil; 9 | import com.intellij.util.ProcessingContext; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jusecase.jte.intellij.language.JteIcons; 12 | import org.jusecase.jte.intellij.language.psi.JtePsiTemplateName; 13 | 14 | public abstract class AbstractTemplateCompletionProvider extends CompletionProvider { 15 | 16 | private final String fileSuffix; 17 | 18 | protected AbstractTemplateCompletionProvider(String fileSuffix) { 19 | this.fileSuffix = fileSuffix; 20 | } 21 | 22 | @Override 23 | protected void addCompletions(@NotNull CompletionParameters parameters, @NotNull ProcessingContext context, @NotNull CompletionResultSet result) { 24 | PsiElement position = parameters.getPosition(); 25 | if (position.getParent() == null) { 26 | return; 27 | } 28 | 29 | if (!(position.getParent() instanceof JtePsiTemplateName)) { 30 | return; 31 | } 32 | JtePsiTemplateName nameElement = (JtePsiTemplateName) position.getParent(); 33 | PsiDirectory rootDirectory = nameElement.findRootDirectory(); 34 | if (rootDirectory == null) { 35 | return; 36 | } 37 | 38 | JtePsiTemplateName prevNameElement = PsiTreeUtil.getPrevSiblingOfType(nameElement, JtePsiTemplateName.class); 39 | if (prevNameElement == null) { 40 | addSuggestionsForDirectory(rootDirectory, result); 41 | } else { 42 | PsiReference reference = prevNameElement.getReference(); 43 | if (reference == null) { 44 | return; 45 | } 46 | 47 | PsiElement prevReferenceElement = reference.resolve(); 48 | if (prevReferenceElement instanceof PsiDirectory) { 49 | addSuggestionsForDirectory((PsiDirectory) prevReferenceElement, result); 50 | } 51 | } 52 | } 53 | 54 | private void addSuggestionsForDirectory(@NotNull PsiDirectory directory, @NotNull CompletionResultSet result) { 55 | for (PsiDirectory subdirectory : directory.getSubdirectories()) { 56 | result.addElement(LookupElementBuilder.create(subdirectory).withIcon(AllIcons.Nodes.Folder)); 57 | } 58 | 59 | addSuggestionsForDirectoryRecursively("", directory, result); 60 | } 61 | 62 | private void addSuggestionsForDirectoryRecursively(@NotNull String prefix, @NotNull PsiDirectory directory, @NotNull CompletionResultSet result) { 63 | for (PsiDirectory subdirectory : directory.getSubdirectories()) { 64 | addSuggestionsForDirectoryRecursively(prefix + subdirectory.getName() + ".", subdirectory, result); 65 | } 66 | 67 | for (PsiFile file : directory.getFiles()) { 68 | String referenceName = resolveReferenceName(prefix, file); 69 | if (referenceName == null) { 70 | continue; 71 | } 72 | 73 | result.addElement(LookupElementBuilder.create(referenceName).withInsertHandler(createAfterCompletionInsertHandler(file)).withIcon(JteIcons.ICON)); 74 | } 75 | } 76 | 77 | private String resolveReferenceName(String prefix, PsiFile file) { 78 | String referenceName = resolveReferenceName(prefix, file, fileSuffix); 79 | if (referenceName != null) { 80 | return referenceName; 81 | } 82 | 83 | return resolveReferenceName(prefix, file, ".jte".equals(fileSuffix) ? ".kte" : ".jte"); 84 | } 85 | 86 | private String resolveReferenceName(String prefix, PsiFile file, String fileSuffix) { 87 | String name = file.getName(); 88 | int index = name.lastIndexOf(fileSuffix); 89 | if (index == -1 || !name.endsWith(fileSuffix)) { 90 | return null; 91 | } 92 | 93 | return prefix + name.substring(0, index); 94 | } 95 | 96 | protected abstract InsertHandler createAfterCompletionInsertHandler(PsiFile file); 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/completion/KteTemplateCompletionProvider.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.completion; 2 | 3 | import com.intellij.codeInsight.completion.*; 4 | import com.intellij.codeInsight.lookup.LookupElement; 5 | import com.intellij.codeInsight.template.Template; 6 | import com.intellij.codeInsight.template.TemplateManager; 7 | import com.intellij.codeInsight.template.impl.MacroCallNode; 8 | import com.intellij.codeInsight.template.macro.CompleteMacro; 9 | import com.intellij.openapi.editor.Editor; 10 | import com.intellij.psi.*; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.kotlin.psi.KtParameter; 13 | import org.jetbrains.kotlin.psi.KtParameterList; 14 | import org.jusecase.jte.intellij.language.psi.*; 15 | 16 | import java.util.ArrayList; 17 | import java.util.Collections; 18 | import java.util.List; 19 | 20 | public class KteTemplateCompletionProvider extends AbstractTemplateCompletionProvider { 21 | 22 | protected KteTemplateCompletionProvider() { 23 | super(".kte"); 24 | } 25 | 26 | @Override 27 | protected InsertHandler createAfterCompletionInsertHandler(PsiFile file) { 28 | return new AfterCompletionInsertHandler(file); 29 | } 30 | 31 | private static class AfterCompletionInsertHandler implements InsertHandler { 32 | private final PsiFile templateFile; 33 | 34 | private AfterCompletionInsertHandler(PsiFile templateFile) { 35 | this.templateFile = templateFile; 36 | } 37 | 38 | @SuppressWarnings("DuplicatedCode") 39 | @Override 40 | public void handleInsert(@NotNull InsertionContext context, @NotNull LookupElement item) { 41 | context.setLaterRunnable(() -> { 42 | Editor editor = context.getEditor(); 43 | int offset = context.getTailOffset(); 44 | boolean needsParenthesis = true; 45 | CharSequence documentText = editor.getDocument().getImmutableCharSequence(); 46 | if (offset < documentText.length() && documentText.charAt(offset) == '(') { 47 | offset += 1; 48 | needsParenthesis = false; 49 | } 50 | 51 | editor.getCaretModel().moveToOffset(offset); 52 | 53 | TemplateManager manager = TemplateManager.getInstance(context.getProject()); 54 | Template template = manager.createTemplate("", ""); 55 | if (needsParenthesis) { 56 | template.addTextSegment("("); 57 | } 58 | 59 | KtParameterList parameterList = KtePsiUtil.resolveParameterList(templateFile); 60 | if (parameterList != null) { 61 | int i = 0; 62 | List parameters = resolveRequiredParams(parameterList); 63 | for (KtParameter parameter : parameters) { 64 | template.addTextSegment(parameter.getName() + " = "); 65 | MacroCallNode param = new MacroCallNode(new CompleteMacro()); 66 | template.addVariable("param" + i, param, param, true); 67 | 68 | if (++i < parameters.size()) { 69 | template.addTextSegment(", "); 70 | } 71 | } 72 | } 73 | 74 | if (needsParenthesis) { 75 | template.addTextSegment(")"); 76 | } 77 | 78 | manager.startTemplate(editor, template); 79 | }); 80 | } 81 | 82 | private List resolveRequiredParams(KtParameterList parameterList) { 83 | List parameters = parameterList.getParameters(); 84 | if (parameters.isEmpty()) { 85 | return Collections.emptyList(); 86 | } 87 | 88 | List result = new ArrayList<>(); 89 | 90 | for (KtParameter parameter : parameters) { 91 | if (!parameter.hasDefaultValue() && !parameter.isVarArg()) { 92 | result.add(parameter); 93 | } 94 | } 95 | 96 | return result; 97 | } 98 | 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/convert/JteConvertFromJspAction.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.convert; 2 | 3 | import com.intellij.execution.PsiLocation; 4 | import com.intellij.execution.actions.ConfigurationContext; 5 | import com.intellij.execution.application.ApplicationConfiguration; 6 | import com.intellij.execution.executors.DefaultDebugExecutor; 7 | import com.intellij.execution.junit2.info.MethodLocation; 8 | import com.intellij.execution.runners.ExecutionUtil; 9 | import com.intellij.openapi.actionSystem.ActionUpdateThread; 10 | import com.intellij.openapi.actionSystem.AnAction; 11 | import com.intellij.openapi.actionSystem.AnActionEvent; 12 | import com.intellij.openapi.actionSystem.LangDataKeys; 13 | import com.intellij.openapi.module.Module; 14 | import com.intellij.openapi.project.Project; 15 | import com.intellij.openapi.roots.ProjectRootManager; 16 | import com.intellij.psi.JavaPsiFacade; 17 | import com.intellij.psi.PsiClass; 18 | import com.intellij.psi.PsiFile; 19 | import com.intellij.psi.PsiMethod; 20 | import com.intellij.psi.search.GlobalSearchScope; 21 | import com.intellij.psi.search.searches.ClassInheritorsSearch; 22 | import org.jetbrains.annotations.NotNull; 23 | 24 | import java.util.Objects; 25 | 26 | public class JteConvertFromJspAction extends AnAction { 27 | 28 | public static final String ERROR_TITLE = "JSP Converter Error"; 29 | public static final String CONVERTER_BASE_CLASS = "gg.jte.convert.jsp.JspToJteConverter"; 30 | 31 | @Override 32 | public void actionPerformed(@NotNull AnActionEvent e) { 33 | PsiFile psiFile = e.getData(LangDataKeys.PSI_FILE); 34 | if (psiFile == null) { 35 | return; 36 | } 37 | 38 | Project project = psiFile.getProject(); 39 | 40 | GlobalSearchScope searchScope = resolveSearchScope(psiFile, project); 41 | 42 | PsiClass converterBaseClass = JavaPsiFacade.getInstance(project).findClass(CONVERTER_BASE_CLASS, searchScope); 43 | if (converterBaseClass == null) { 44 | JteConvertNotification.error(project, ERROR_TITLE, "Could not locate class '" + CONVERTER_BASE_CLASS + "' on the classpath. You're probably missing the jte-jsp-converter dependency."); 45 | return; 46 | } 47 | 48 | PsiClass converterClass = ClassInheritorsSearch.search(converterBaseClass, searchScope, true).findFirst(); 49 | if (converterClass == null) { 50 | JteConvertNotification.error(project, ERROR_TITLE, "Could not locate a class implementing '" + CONVERTER_BASE_CLASS + "'. You need to implement it, to customize JSP conversion for your specific project."); 51 | return; 52 | } 53 | 54 | @NotNull PsiMethod[] mainMethods = converterClass.findMethodsByName("main", false); 55 | if (mainMethods.length == 0) { 56 | JteConvertNotification.error(project, ERROR_TITLE, "You need to create a main method in '" + converterClass.getQualifiedName() + "'. In the main method you need to init the converter and customize JSP conversion for your specific project."); 57 | return; 58 | } 59 | 60 | MethodLocation location = new MethodLocation(project, mainMethods[0], new PsiLocation<>(converterClass)); 61 | ConfigurationContext configurationContext = ConfigurationContext.createEmptyContextForLocation(location); 62 | 63 | ApplicationConfiguration configuration = (ApplicationConfiguration)Objects.requireNonNull(configurationContext.getConfiguration()).getConfiguration(); 64 | configuration.setProgramParameters(psiFile.getOriginalFile().getVirtualFile().getPath()); 65 | 66 | ExecutionUtil.runConfiguration(Objects.requireNonNull(configurationContext.getConfiguration()), DefaultDebugExecutor.getDebugExecutorInstance()); 67 | } 68 | 69 | @NotNull 70 | private GlobalSearchScope resolveSearchScope(PsiFile psiFile, Project project) { 71 | Module module = ProjectRootManager.getInstance(project).getFileIndex().getModuleForFile(psiFile.getVirtualFile()); 72 | if (module != null) { 73 | return GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module); 74 | } else { 75 | return GlobalSearchScope.everythingScope(project); 76 | } 77 | } 78 | 79 | @Override 80 | public void update(@NotNull AnActionEvent e) { 81 | PsiFile psiFile = e.getData(LangDataKeys.PSI_FILE); 82 | if (psiFile == null) { 83 | return; 84 | } 85 | 86 | e.getPresentation().setEnabledAndVisible("JSP".equals(psiFile.getFileType().getName())); 87 | } 88 | 89 | @Override 90 | public @NotNull ActionUpdateThread getActionUpdateThread() { 91 | return ActionUpdateThread.BGT; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/completion/KteTemplateParamCompletionProvider.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.completion; 2 | 3 | import com.intellij.codeInsight.completion.CompletionParameters; 4 | import com.intellij.codeInsight.completion.CompletionProvider; 5 | import com.intellij.codeInsight.completion.CompletionResultSet; 6 | import com.intellij.codeInsight.lookup.LookupElementBuilder; 7 | import com.intellij.lang.injection.InjectedLanguageManager; 8 | import com.intellij.psi.PsiElement; 9 | import com.intellij.psi.PsiFile; 10 | import com.intellij.psi.impl.source.resolve.FileContextUtil; 11 | import com.intellij.psi.impl.source.tree.LeafPsiElement; 12 | import com.intellij.psi.tree.IElementType; 13 | import com.intellij.psi.util.PsiTreeUtil; 14 | import com.intellij.util.ProcessingContext; 15 | import org.jetbrains.annotations.NotNull; 16 | import org.jetbrains.annotations.Nullable; 17 | import org.jetbrains.kotlin.psi.KtParameter; 18 | import org.jetbrains.kotlin.psi.KtParameterList; 19 | import org.jusecase.jte.intellij.language.parsing.KteTokenTypes; 20 | import org.jusecase.jte.intellij.language.psi.*; 21 | 22 | import java.util.Set; 23 | import java.util.stream.Collectors; 24 | 25 | public class KteTemplateParamCompletionProvider extends CompletionProvider { 26 | private final boolean kotlin; 27 | 28 | public KteTemplateParamCompletionProvider(boolean kotlin) { 29 | this.kotlin = kotlin; 30 | } 31 | 32 | @Override 33 | protected void addCompletions(@NotNull CompletionParameters parameters, @NotNull ProcessingContext context, @NotNull CompletionResultSet result) { 34 | PsiElement jteElement = getPsiElement(parameters); 35 | if (jteElement == null) { 36 | return; 37 | } 38 | 39 | if (jteElement.getNode().getElementType() == KteTokenTypes.PARAM_NAME) { 40 | // The user already started typing, that's okay 41 | jteElement = jteElement.getParent(); 42 | } else { 43 | PsiElement prevSibling = JtePsiUtil.getPrevSiblingIgnoring(jteElement, KteTokenTypes.WHITESPACE); 44 | if (prevSibling == null) { 45 | return; 46 | } 47 | IElementType elementType = prevSibling.getNode().getElementType(); 48 | if (elementType != KteTokenTypes.PARAMS_BEGIN && elementType != KteTokenTypes.COMMA && elementType != KteTokenTypes.JAVA_INJECTION) { 49 | return; 50 | } 51 | } 52 | 53 | JtePsiTemplateName templateName = PsiTreeUtil.getPrevSiblingOfType(jteElement, JtePsiTemplateName.class); 54 | if (templateName == null) { 55 | return; 56 | } 57 | 58 | PsiFile templateFile = templateName.resolveFile(); 59 | if (templateFile == null) { 60 | return; 61 | } 62 | 63 | KtParameterList parameterList = KtePsiUtil.resolveParameterList(templateFile); 64 | if (parameterList == null) { 65 | return; 66 | } 67 | 68 | Set usedNames = PsiTreeUtil.findChildrenOfType(templateName.getParent(), JtePsiParamName.class).stream().map(JtePsiParamName::getName).collect(Collectors.toSet()); 69 | for (KtParameter parameter : parameterList.getParameters()) { 70 | if (parameter.isVarArg()) { 71 | continue; 72 | } 73 | if (!usedNames.contains(parameter.getName())) { 74 | result.addElement(LookupElementBuilder.create(parameter.getName() + " = ")); 75 | } 76 | } 77 | } 78 | 79 | @Nullable 80 | private PsiElement getPsiElement(@NotNull CompletionParameters parameters) { 81 | if (kotlin) { 82 | return getKotlinPsiElement(parameters); 83 | } else { 84 | return getJtePsiElement(parameters); 85 | } 86 | } 87 | 88 | @Nullable 89 | private PsiElement getJtePsiElement(CompletionParameters parameters) { 90 | return parameters.getOriginalPosition(); 91 | } 92 | 93 | @Nullable 94 | private PsiElement getKotlinPsiElement(@NotNull CompletionParameters parameters) { 95 | PsiElement fileContext = FileContextUtil.getFileContext(parameters.getOriginalFile()); 96 | if (fileContext == null) { 97 | return null; 98 | } 99 | 100 | PsiElement originalPosition = parameters.getPosition(); 101 | 102 | int injectionOffsetInMasterFile = InjectedLanguageManager.getInstance(fileContext.getProject()).injectedToHost(originalPosition, parameters.getOffset(), false); 103 | PsiElement result = fileContext.getContainingFile().findElementAt(injectionOffsetInMasterFile); 104 | 105 | if (result instanceof LeafPsiElement && result.getParent() instanceof JtePsiJavaInjection) { 106 | return result.getParent(); 107 | } 108 | return result; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/completion/JteTemplateParamCompletionProvider.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.completion; 2 | 3 | import com.intellij.codeInsight.completion.CompletionParameters; 4 | import com.intellij.codeInsight.completion.CompletionProvider; 5 | import com.intellij.codeInsight.completion.CompletionResultSet; 6 | import com.intellij.codeInsight.lookup.LookupElementBuilder; 7 | import com.intellij.lang.injection.InjectedLanguageManager; 8 | import com.intellij.psi.PsiElement; 9 | import com.intellij.psi.PsiFile; 10 | import com.intellij.psi.PsiParameter; 11 | import com.intellij.psi.PsiParameterList; 12 | import com.intellij.psi.impl.source.resolve.FileContextUtil; 13 | import com.intellij.psi.impl.source.tree.LeafPsiElement; 14 | import com.intellij.psi.tree.IElementType; 15 | import com.intellij.psi.util.PsiTreeUtil; 16 | import com.intellij.util.ProcessingContext; 17 | import org.jetbrains.annotations.NotNull; 18 | import org.jetbrains.annotations.Nullable; 19 | import org.jusecase.jte.intellij.language.parsing.JteTokenTypes; 20 | import org.jusecase.jte.intellij.language.psi.JtePsiJavaInjection; 21 | import org.jusecase.jte.intellij.language.psi.JtePsiParamName; 22 | import org.jusecase.jte.intellij.language.psi.JtePsiTemplateName; 23 | import org.jusecase.jte.intellij.language.psi.JtePsiUtil; 24 | 25 | import java.util.Set; 26 | import java.util.stream.Collectors; 27 | 28 | public class JteTemplateParamCompletionProvider extends CompletionProvider { 29 | private final boolean java; 30 | 31 | public JteTemplateParamCompletionProvider(boolean java) { 32 | this.java = java; 33 | } 34 | 35 | @Override 36 | protected void addCompletions(@NotNull CompletionParameters parameters, @NotNull ProcessingContext context, @NotNull CompletionResultSet result) { 37 | PsiElement jteElement = getPsiElement(parameters); 38 | if (jteElement == null) { 39 | return; 40 | } 41 | 42 | if (jteElement.getNode().getElementType() == JteTokenTypes.PARAM_NAME) { 43 | // The user already started typing, that's okay 44 | jteElement = jteElement.getParent(); 45 | } else { 46 | PsiElement prevSibling = JtePsiUtil.getPrevSiblingIgnoring(jteElement, JteTokenTypes.WHITESPACE); 47 | if (prevSibling == null) { 48 | return; 49 | } 50 | IElementType elementType = prevSibling.getNode().getElementType(); 51 | if (elementType != JteTokenTypes.PARAMS_BEGIN && elementType != JteTokenTypes.COMMA && elementType != JteTokenTypes.JAVA_INJECTION) { 52 | return; 53 | } 54 | } 55 | 56 | JtePsiTemplateName templateName = PsiTreeUtil.getPrevSiblingOfType(jteElement, JtePsiTemplateName.class); 57 | if (templateName == null) { 58 | return; 59 | } 60 | 61 | PsiFile templateFile = templateName.resolveFile(); 62 | if (templateFile == null) { 63 | return; 64 | } 65 | 66 | PsiParameterList parameterList = JtePsiUtil.resolveParameterList(templateFile); 67 | if (parameterList == null) { 68 | return; 69 | } 70 | 71 | Set usedNames = PsiTreeUtil.findChildrenOfType(templateName.getParent(), JtePsiParamName.class).stream().map(JtePsiParamName::getName).collect(Collectors.toSet()); 72 | for (PsiParameter parameter : parameterList.getParameters()) { 73 | if (parameter.isVarArgs()) { 74 | continue; 75 | } 76 | if (!usedNames.contains(parameter.getName())) { 77 | result.addElement(LookupElementBuilder.create(parameter.getName() + " = ").withTypeText(parameter.getType().getPresentableText())); 78 | } 79 | } 80 | } 81 | 82 | @Nullable 83 | private PsiElement getPsiElement(@NotNull CompletionParameters parameters) { 84 | if (java) { 85 | return getJavaPsiElement(parameters); 86 | } else { 87 | return getJtePsiElement(parameters); 88 | } 89 | } 90 | 91 | @Nullable 92 | private PsiElement getJtePsiElement(CompletionParameters parameters) { 93 | return parameters.getOriginalPosition(); 94 | } 95 | 96 | @Nullable 97 | private PsiElement getJavaPsiElement(@NotNull CompletionParameters parameters) { 98 | PsiElement fileContext = FileContextUtil.getFileContext(parameters.getOriginalFile()); 99 | if (fileContext == null) { 100 | return null; 101 | } 102 | 103 | PsiElement originalPosition = parameters.getPosition(); 104 | 105 | int injectionOffsetInMasterFile = InjectedLanguageManager.getInstance(fileContext.getProject()).injectedToHost(originalPosition, parameters.getOffset(), false); 106 | PsiElement result = fileContext.getContainingFile().findElementAt(injectionOffsetInMasterFile); 107 | 108 | if (result instanceof LeafPsiElement && result.getParent() instanceof JtePsiJavaInjection) { 109 | return result.getParent(); 110 | } 111 | return result; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/completion/JteTemplateCompletionProvider.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.completion; 2 | 3 | import com.intellij.codeInsight.completion.*; 4 | import com.intellij.codeInsight.lookup.LookupElement; 5 | import com.intellij.codeInsight.template.Template; 6 | import com.intellij.codeInsight.template.TemplateManager; 7 | import com.intellij.codeInsight.template.impl.MacroCallNode; 8 | import com.intellij.codeInsight.template.macro.CompleteMacro; 9 | import com.intellij.openapi.editor.Editor; 10 | import com.intellij.psi.*; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jusecase.jte.intellij.language.psi.JtePsiExtraJavaInjection; 13 | import org.jusecase.jte.intellij.language.psi.JtePsiParam; 14 | import org.jusecase.jte.intellij.language.psi.JtePsiUtil; 15 | 16 | import java.util.ArrayList; 17 | import java.util.Collections; 18 | import java.util.List; 19 | import java.util.concurrent.atomic.AtomicInteger; 20 | 21 | public class JteTemplateCompletionProvider extends AbstractTemplateCompletionProvider { 22 | 23 | protected JteTemplateCompletionProvider() { 24 | super(".jte"); 25 | } 26 | 27 | @Override 28 | protected InsertHandler createAfterCompletionInsertHandler(PsiFile file) { 29 | return new AfterCompletionInsertHandler(file); 30 | } 31 | 32 | private static class AfterCompletionInsertHandler implements InsertHandler { 33 | private final PsiFile templateFile; 34 | 35 | private AfterCompletionInsertHandler(PsiFile templateFile) { 36 | this.templateFile = templateFile; 37 | } 38 | 39 | @SuppressWarnings("DuplicatedCode") 40 | @Override 41 | public void handleInsert(@NotNull InsertionContext context, @NotNull LookupElement item) { 42 | context.setLaterRunnable(() -> { 43 | Editor editor = context.getEditor(); 44 | int offset = context.getTailOffset(); 45 | boolean needsParenthesis = true; 46 | CharSequence documentText = editor.getDocument().getImmutableCharSequence(); 47 | if (offset < documentText.length() && documentText.charAt(offset) == '(') { 48 | offset += 1; 49 | needsParenthesis = false; 50 | } 51 | 52 | editor.getCaretModel().moveToOffset(offset); 53 | 54 | TemplateManager manager = TemplateManager.getInstance(context.getProject()); 55 | Template template = manager.createTemplate("", ""); 56 | if (needsParenthesis) { 57 | template.addTextSegment("("); 58 | } 59 | 60 | PsiParameterList parameterList = JtePsiUtil.resolveParameterList(templateFile); 61 | if (parameterList != null) { 62 | int i = 0; 63 | List parameters = resolveRequiredParams(parameterList); 64 | for (PsiParameter parameter : parameters) { 65 | template.addTextSegment(parameter.getName() + " = "); 66 | MacroCallNode param = new MacroCallNode(new CompleteMacro()); 67 | template.addVariable("param" + i, param, param, true); 68 | 69 | if (++i < parameters.size()) { 70 | template.addTextSegment(", "); 71 | } 72 | } 73 | } 74 | 75 | if (needsParenthesis) { 76 | template.addTextSegment(")"); 77 | } 78 | 79 | manager.startTemplate(editor, template); 80 | }); 81 | } 82 | 83 | private List resolveRequiredParams(PsiParameterList parameterList) { 84 | PsiParameter[] parameters = parameterList.getParameters(); 85 | if (parameters.length == 0) { 86 | return Collections.emptyList(); 87 | } 88 | 89 | List result = new ArrayList<>(); 90 | 91 | boolean[] defaultParams = resolveDefaultParams(parameters.length); 92 | for (int i = 0; i < parameters.length; i++) { 93 | if (!defaultParams[i]) { 94 | PsiParameter parameter = parameters[i]; 95 | if (!parameter.isVarArgs()) { 96 | result.add(parameter); 97 | } 98 | } 99 | } 100 | 101 | return result; 102 | } 103 | 104 | private boolean[] resolveDefaultParams(int paramLength) { 105 | boolean[] defaultParams = new boolean[paramLength]; 106 | AtomicInteger index = new AtomicInteger(); 107 | 108 | SyntaxTraverser.psiTraverser(templateFile).filter(JtePsiParam.class).forEach(param -> { 109 | if (index.get() >= defaultParams.length) { 110 | return; 111 | } 112 | 113 | defaultParams[index.getAndIncrement()] = param.getLastChild() instanceof JtePsiExtraJavaInjection; 114 | }); 115 | 116 | return defaultParams; 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/JteFileViewProvider.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language; 2 | 3 | import com.intellij.ide.highlighter.HtmlFileType; 4 | import com.intellij.lang.Language; 5 | import com.intellij.lang.LanguageParserDefinitions; 6 | import com.intellij.lang.ParserDefinition; 7 | import com.intellij.openapi.vfs.VirtualFile; 8 | import com.intellij.psi.LanguageSubstitutors; 9 | import com.intellij.psi.MultiplePsiFilesPerDocumentFileViewProvider; 10 | import com.intellij.psi.PsiFile; 11 | import com.intellij.psi.PsiManager; 12 | import com.intellij.psi.impl.source.PsiFileImpl; 13 | import com.intellij.psi.templateLanguages.ConfigurableTemplateLanguageFileViewProvider; 14 | import com.intellij.psi.templateLanguages.TemplateDataElementType; 15 | import com.intellij.psi.templateLanguages.TemplateDataLanguageMappings; 16 | import com.intellij.psi.tree.IElementType; 17 | import org.jetbrains.annotations.NotNull; 18 | import org.jetbrains.annotations.Nullable; 19 | import org.jusecase.jte.intellij.language.parsing.TokenTypes; 20 | 21 | import java.util.Set; 22 | import java.util.concurrent.ConcurrentHashMap; 23 | import java.util.concurrent.ConcurrentMap; 24 | import java.util.function.Function; 25 | 26 | public class JteFileViewProvider extends MultiplePsiFilesPerDocumentFileViewProvider implements ConfigurableTemplateLanguageFileViewProvider { 27 | 28 | private static final ConcurrentMap TEMPLATE_DATA_TO_LANG = new ConcurrentHashMap<>(); 29 | 30 | @NotNull 31 | private static Language getTemplateDataLanguage(PsiManager manager, VirtualFile file) { 32 | Language dataLang = TemplateDataLanguageMappings.getInstance(manager.getProject()).getMapping(file); 33 | if (dataLang == null) { 34 | dataLang = HtmlFileType.INSTANCE.getLanguage(); 35 | } 36 | 37 | Language substituteLang = LanguageSubstitutors.getInstance().substituteLanguage(dataLang, file, manager.getProject()); 38 | 39 | // only use a substituted language if it's templateable 40 | if (TemplateDataLanguageMappings.getTemplateableLanguages().contains(substituteLang)) { 41 | dataLang = substituteLang; 42 | } 43 | 44 | return dataLang; 45 | } 46 | 47 | private TemplateDataElementType getTemplateDataElementType(Language lang, Function provider) { 48 | return TEMPLATE_DATA_TO_LANG.computeIfAbsent(lang.getID() + myTokenTypes, provider); 49 | } 50 | 51 | private final Language myBaseLanguage; 52 | private final Language myTemplateLanguage; 53 | private final TokenTypes myTokenTypes; 54 | 55 | public JteFileViewProvider(@NotNull PsiManager manager, @NotNull VirtualFile virtualFile, boolean eventSystemEnabled, Language language, TokenTypes tokenTypes) { 56 | this(manager, virtualFile, eventSystemEnabled, language, getTemplateDataLanguage(manager, virtualFile), tokenTypes); 57 | } 58 | 59 | private JteFileViewProvider(@NotNull PsiManager manager, @NotNull VirtualFile virtualFile, boolean eventSystemEnabled, Language myBaseLanguage, Language myTemplateLanguage, TokenTypes tokenTypes) { 60 | super(manager, virtualFile, eventSystemEnabled); 61 | 62 | this.myBaseLanguage = myBaseLanguage; 63 | this.myTemplateLanguage = myTemplateLanguage; 64 | this.myTokenTypes = tokenTypes; 65 | } 66 | 67 | @NotNull 68 | @Override 69 | public Language getBaseLanguage() { 70 | return myBaseLanguage; 71 | } 72 | 73 | @NotNull 74 | @Override 75 | public Language getTemplateDataLanguage() { 76 | return myTemplateLanguage; 77 | } 78 | 79 | @NotNull 80 | @Override 81 | public Set getLanguages() { 82 | return Set.of(myBaseLanguage, getTemplateDataLanguage()); 83 | } 84 | 85 | @NotNull 86 | @Override 87 | protected MultiplePsiFilesPerDocumentFileViewProvider cloneInner(@NotNull VirtualFile fileCopy) { 88 | return new JteFileViewProvider(getManager(), fileCopy, false, myBaseLanguage, myTemplateLanguage, myTokenTypes); 89 | } 90 | 91 | @Nullable 92 | @Override 93 | protected PsiFile createFile(@NotNull Language lang) { 94 | ParserDefinition parserDefinition = getDefinition(lang); 95 | if (parserDefinition == null) { 96 | return null; 97 | } 98 | 99 | if (lang.is(getTemplateDataLanguage())) { 100 | PsiFile file = parserDefinition.createFile(this); 101 | IElementType type = getContentElementType(lang); 102 | if (type != null) { 103 | ((PsiFileImpl) file).setContentElementType(type); 104 | } 105 | return file; 106 | } else if (lang.isKindOf(getBaseLanguage())) { 107 | return parserDefinition.createFile(this); 108 | } 109 | return null; 110 | } 111 | 112 | @SuppressWarnings("UnstableApiUsage") 113 | @Override 114 | public IElementType getContentElementType(@NotNull Language language) { 115 | if (language.is(getTemplateDataLanguage())) { 116 | return getTemplateDataElementType(language, s -> new JteTemplateDataElementType(language, myTokenTypes)); 117 | } 118 | return null; 119 | } 120 | 121 | private ParserDefinition getDefinition(Language lang) { 122 | if (lang.isKindOf(getBaseLanguage())) { 123 | return LanguageParserDefinitions.INSTANCE.forLanguage(lang.is(getBaseLanguage()) ? lang : getBaseLanguage()); 124 | } else { 125 | return LanguageParserDefinitions.INSTANCE.forLanguage(lang); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/JteParserDefinition.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.intellij.lang.ParserDefinition; 5 | import com.intellij.lang.PsiParser; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.psi.FileViewProvider; 8 | import com.intellij.psi.PsiElement; 9 | import com.intellij.psi.PsiFile; 10 | import com.intellij.psi.tree.IElementType; 11 | import com.intellij.psi.tree.IFileElementType; 12 | import com.intellij.psi.tree.TokenSet; 13 | import org.jetbrains.annotations.NotNull; 14 | import org.jusecase.jte.intellij.language.psi.*; 15 | 16 | public class JteParserDefinition implements ParserDefinition { 17 | 18 | @NotNull 19 | @Override 20 | public Lexer createLexer(Project project) { 21 | return new JteLexer(); 22 | } 23 | 24 | @Override 25 | public PsiParser createParser(Project project) { 26 | return new JteParser(); 27 | } 28 | 29 | @Override 30 | public IFileElementType getFileNodeType() { 31 | return JteTokenTypes.FILE; 32 | } 33 | 34 | @NotNull 35 | @Override 36 | public TokenSet getCommentTokens() { 37 | return JteTokenTypes.COMMENTS; 38 | } 39 | 40 | @NotNull 41 | @Override 42 | public TokenSet getStringLiteralElements() { 43 | return JteTokenTypes.STRING_LITERALS; 44 | } 45 | 46 | @Override 47 | public @NotNull TokenSet getWhitespaceTokens() { 48 | return JteTokenTypes.WHITESPACES; 49 | } 50 | 51 | @NotNull 52 | @Override 53 | public PsiElement createElement(ASTNode node) { 54 | IElementType elementType = node.getElementType(); 55 | 56 | if (elementType == JteTokenTypes.JAVA_CONTENT) { 57 | return new JtePsiJavaContent(node); 58 | } else if (elementType == JteTokenTypes.JAVA_INJECTION) { 59 | return new JtePsiJavaInjection(node); 60 | } else if (elementType == JteTokenTypes.PARAM) { 61 | return new JtePsiParam(node); 62 | } else if (elementType == JteTokenTypes.IMPORT) { 63 | return new JtePsiImport(node); 64 | } else if (elementType == JteTokenTypes.OUTPUT) { 65 | return new JtePsiOutput(node); 66 | } else if (elementType == JteTokenTypes.STATEMENT) { 67 | return new JtePsiStatement(node); 68 | } else if (elementType == JteTokenTypes.CONDITION_BEGIN) { 69 | return new JtePsiConditionBegin(node); 70 | } else if (elementType == JteTokenTypes.CONDITION_END) { 71 | return new JtePsiConditionEnd(node); 72 | } else if (elementType == JteTokenTypes.IF) { 73 | return new JtePsiIf(node); 74 | } else if (elementType == JteTokenTypes.ELSE) { 75 | return new JtePsiElse(node); 76 | } else if (elementType == JteTokenTypes.ELSEIF) { 77 | return new JtePsiElseIf(node); 78 | } else if (elementType == JteTokenTypes.ENDIF) { 79 | return new JtePsiEndIf(node); 80 | } else if (elementType == JteTokenTypes.FOR) { 81 | return new JtePsiFor(node); 82 | } else if (elementType == JteTokenTypes.ENDFOR) { 83 | return new JtePsiEndFor(node); 84 | } else if (elementType == JteTokenTypes.TEMPLATE) { 85 | return new JtePsiTemplate(node); 86 | } else if (elementType == JteTokenTypes.TEMPLATE_NAME) { 87 | return new JtePsiTemplateName(node, ".jte"); 88 | } else if (elementType == JteTokenTypes.PARAMS_BEGIN) { 89 | return new JtePsiParamsBegin(node); 90 | } else if (elementType == JteTokenTypes.PARAMS_END) { 91 | return new JtePsiParamsEnd(node); 92 | } else if (elementType == JteTokenTypes.NAME_SEPARATOR) { 93 | return new JtePsiNameSeparator(node); 94 | } else if (elementType == JteTokenTypes.OUTPUT_BEGIN) { 95 | return new JtePsiOutputBegin(node); 96 | } else if (elementType == JteTokenTypes.OUTPUT_END) { 97 | return new JtePsiOutputEnd(node); 98 | } else if (elementType == JteTokenTypes.STATEMENT_BEGIN) { 99 | return new JtePsiStatementBegin(node); 100 | } else if (elementType == JteTokenTypes.STATEMENT_END) { 101 | return new JtePsiStatementEnd(node); 102 | } else if (elementType == JteTokenTypes.COMMENT) { 103 | return new JtePsiComment(node); 104 | } else if (elementType == JteTokenTypes.EQUALS) { 105 | return new JtePsiEquals(node); 106 | } else if (elementType == JteTokenTypes.EXTRA_JAVA_INJECTION) { 107 | return new JtePsiExtraJavaInjection(node); 108 | } else if (elementType == JteTokenTypes.COMMA) { 109 | return new JtePsiComma(node); 110 | } else if (elementType == JteTokenTypes.PARAM_NAME) { 111 | return new JtePsiParamName(node); 112 | } else if (elementType == JteTokenTypes.CONTENT_BEGIN) { 113 | return new JtePsiContent(node); 114 | } else if (elementType == JteTokenTypes.CONTENT_END) { 115 | return new JtePsiEndContent(node); 116 | } else if (elementType == JteTokenTypes.BLOCK) { 117 | return new JtePsiBlock(node); 118 | } 119 | 120 | return new JtePsiElement(node); 121 | } 122 | 123 | @Override 124 | public PsiFile createFile(FileViewProvider viewProvider) { 125 | return new JtePsiFile(viewProvider); 126 | } 127 | 128 | @Override 129 | public SpaceRequirements spaceExistenceTypeBetweenTokens(ASTNode left, ASTNode right) { 130 | return SpaceRequirements.MAY; 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/KteParserDefinition.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.intellij.lang.ParserDefinition; 5 | import com.intellij.lang.PsiParser; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.psi.FileViewProvider; 8 | import com.intellij.psi.PsiElement; 9 | import com.intellij.psi.PsiFile; 10 | import com.intellij.psi.tree.IElementType; 11 | import com.intellij.psi.tree.IFileElementType; 12 | import com.intellij.psi.tree.TokenSet; 13 | import org.jetbrains.annotations.NotNull; 14 | import org.jusecase.jte.intellij.language.psi.*; 15 | 16 | public class KteParserDefinition implements ParserDefinition { 17 | 18 | @NotNull 19 | @Override 20 | public Lexer createLexer(Project project) { 21 | return new KteLexer(); 22 | } 23 | 24 | @Override 25 | public PsiParser createParser(Project project) { 26 | return new KteParser(); 27 | } 28 | 29 | @Override 30 | public IFileElementType getFileNodeType() { 31 | return KteTokenTypes.FILE; 32 | } 33 | 34 | @NotNull 35 | @Override 36 | public TokenSet getCommentTokens() { 37 | return KteTokenTypes.COMMENTS; 38 | } 39 | 40 | @NotNull 41 | @Override 42 | public TokenSet getStringLiteralElements() { 43 | return KteTokenTypes.STRING_LITERALS; 44 | } 45 | 46 | @Override 47 | public @NotNull TokenSet getWhitespaceTokens() { 48 | return KteTokenTypes.WHITESPACES; 49 | } 50 | 51 | @NotNull 52 | @Override 53 | public PsiElement createElement(ASTNode node) { 54 | IElementType elementType = node.getElementType(); 55 | 56 | if (elementType == KteTokenTypes.JAVA_CONTENT) { 57 | return new KtePsiJavaContent(node); 58 | } else if (elementType == KteTokenTypes.JAVA_INJECTION) { 59 | return new JtePsiJavaInjection(node); 60 | } else if (elementType == KteTokenTypes.PARAM) { 61 | return new JtePsiParam(node); 62 | } else if (elementType == KteTokenTypes.IMPORT) { 63 | return new JtePsiImport(node); 64 | } else if (elementType == KteTokenTypes.OUTPUT) { 65 | return new JtePsiOutput(node); 66 | } else if (elementType == KteTokenTypes.STATEMENT) { 67 | return new JtePsiStatement(node); 68 | } else if (elementType == KteTokenTypes.CONDITION_BEGIN) { 69 | return new JtePsiConditionBegin(node); 70 | } else if (elementType == KteTokenTypes.CONDITION_END) { 71 | return new JtePsiConditionEnd(node); 72 | } else if (elementType == KteTokenTypes.IF) { 73 | return new JtePsiIf(node); 74 | } else if (elementType == KteTokenTypes.ELSE) { 75 | return new JtePsiElse(node); 76 | } else if (elementType == KteTokenTypes.ELSEIF) { 77 | return new JtePsiElseIf(node); 78 | } else if (elementType == KteTokenTypes.ENDIF) { 79 | return new JtePsiEndIf(node); 80 | } else if (elementType == KteTokenTypes.FOR) { 81 | return new JtePsiFor(node); 82 | } else if (elementType == KteTokenTypes.ENDFOR) { 83 | return new JtePsiEndFor(node); 84 | } else if (elementType == KteTokenTypes.TEMPLATE) { 85 | return new JtePsiTemplate(node); 86 | } else if (elementType == KteTokenTypes.TEMPLATE_NAME) { 87 | return new JtePsiTemplateName(node, ".kte"); 88 | } else if (elementType == KteTokenTypes.PARAMS_BEGIN) { 89 | return new JtePsiParamsBegin(node); 90 | } else if (elementType == KteTokenTypes.PARAMS_END) { 91 | return new JtePsiParamsEnd(node); 92 | } else if (elementType == KteTokenTypes.NAME_SEPARATOR) { 93 | return new JtePsiNameSeparator(node); 94 | } else if (elementType == KteTokenTypes.OUTPUT_BEGIN) { 95 | return new JtePsiOutputBegin(node); 96 | } else if (elementType == KteTokenTypes.OUTPUT_END) { 97 | return new JtePsiOutputEnd(node); 98 | } else if (elementType == KteTokenTypes.STATEMENT_BEGIN) { 99 | return new JtePsiStatementBegin(node); 100 | } else if (elementType == KteTokenTypes.STATEMENT_END) { 101 | return new JtePsiStatementEnd(node); 102 | } else if (elementType == KteTokenTypes.COMMENT) { 103 | return new JtePsiComment(node); 104 | } else if (elementType == KteTokenTypes.EQUALS) { 105 | return new JtePsiEquals(node); 106 | } else if (elementType == KteTokenTypes.EXTRA_JAVA_INJECTION) { 107 | return new JtePsiExtraJavaInjection(node); 108 | } else if (elementType == KteTokenTypes.COMMA) { 109 | return new JtePsiComma(node); 110 | } else if (elementType == KteTokenTypes.PARAM_NAME) { 111 | return new KtePsiParamName(node); 112 | } else if (elementType == KteTokenTypes.CONTENT_BEGIN) { 113 | return new JtePsiContent(node); 114 | } else if (elementType == KteTokenTypes.CONTENT_END) { 115 | return new JtePsiEndContent(node); 116 | } else if (elementType == KteTokenTypes.BLOCK) { 117 | return new JtePsiBlock(node); 118 | } 119 | 120 | return new JtePsiElement(node); 121 | } 122 | 123 | @Override 124 | public PsiFile createFile(FileViewProvider viewProvider) { 125 | return new KtePsiFile(viewProvider); 126 | } 127 | 128 | @Override 129 | public SpaceRequirements spaceExistenceTypeBetweenTokens(ASTNode left, ASTNode right) { 130 | return SpaceRequirements.MAY; 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/refactoring/JteMoveFileHandler.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.refactoring; 2 | 3 | import com.intellij.lang.*; 4 | import com.intellij.lexer.Lexer; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.util.Comparing; 7 | import com.intellij.openapi.util.TextRange; 8 | import com.intellij.openapi.vfs.VirtualFile; 9 | import com.intellij.psi.*; 10 | import com.intellij.psi.impl.source.DummyHolder; 11 | import com.intellij.psi.impl.source.DummyHolderFactory; 12 | import com.intellij.psi.impl.source.codeStyle.CodeEditUtil; 13 | import com.intellij.psi.impl.source.tree.TreeElement; 14 | import com.intellij.psi.search.GlobalSearchScope; 15 | import com.intellij.psi.search.searches.ReferencesSearch; 16 | import com.intellij.psi.util.PsiTreeUtil; 17 | import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFileHandler; 18 | import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesUtil; 19 | import com.intellij.refactoring.util.MoveRenameUsageInfo; 20 | import com.intellij.usageView.UsageInfo; 21 | import com.intellij.util.IncorrectOperationException; 22 | import org.jetbrains.annotations.NotNull; 23 | import org.jetbrains.annotations.Nullable; 24 | import org.jusecase.jte.intellij.language.parsing.JteTokenTypes; 25 | import org.jusecase.jte.intellij.language.psi.*; 26 | 27 | import java.util.*; 28 | 29 | public class JteMoveFileHandler extends MoveFileHandler { 30 | 31 | @Override 32 | public boolean canProcessElement(PsiFile element) { 33 | return element instanceof JtePsiFile || element instanceof KtePsiFile; 34 | } 35 | 36 | @Override 37 | public void prepareMovedFile(PsiFile file, PsiDirectory moveDestination, Map oldToNewMap) { 38 | VirtualFile dstDir = moveDestination.getVirtualFile(); 39 | 40 | final PsiDirectory containingDirectory = file.getContainingDirectory(); 41 | if (!Comparing.equal(dstDir, containingDirectory != null ? containingDirectory.getVirtualFile() : null)) { 42 | MoveFilesOrDirectoriesUtil.doMoveFile(file, moveDestination); 43 | } 44 | } 45 | 46 | @Override 47 | public @Nullable List findUsages(PsiFile psiFile, PsiDirectory newParent, boolean searchInComments, boolean searchInNonJavaFiles) { 48 | List result = new ArrayList<>(); 49 | 50 | Set foundReferences = new HashSet<>(); 51 | 52 | for (PsiReference reference : ReferencesSearch.search(psiFile, GlobalSearchScope.projectScope(psiFile.getProject()), false)) { 53 | if (foundReferences.contains(reference)) { 54 | continue; 55 | } 56 | TextRange range = reference.getRangeInElement(); 57 | result.add(new MoveRenameUsageInfo(reference.getElement(), reference, range.getStartOffset(), range.getEndOffset(), psiFile, false)); 58 | foundReferences.add(reference); 59 | } 60 | 61 | return result; 62 | } 63 | 64 | @Override 65 | public void retargetUsages(List usageInfos, Map oldToNewMap) { 66 | for (UsageInfo usage : usageInfos) { 67 | if (usage instanceof MoveRenameUsageInfo) { 68 | retargetUsage((MoveRenameUsageInfo) usage); 69 | } 70 | } 71 | } 72 | 73 | private void retargetUsage(MoveRenameUsageInfo usage) { 74 | PsiReference reference = usage.getReference(); 75 | if (reference == null) { 76 | return; 77 | } 78 | 79 | if (!(usage.getReferencedElement() instanceof PsiFile)) { 80 | return; 81 | } 82 | PsiFile newFile = (PsiFile) usage.getReferencedElement(); 83 | 84 | if (!(reference.getElement() instanceof JtePsiTemplateName)) { 85 | return; 86 | } 87 | JtePsiTemplateName templateName = (JtePsiTemplateName) reference.getElement(); 88 | 89 | PsiDirectory rootDirectory = templateName.findRootDirectory(); 90 | if (rootDirectory == null) { 91 | return; 92 | } 93 | 94 | PsiElement template = reference.getElement().getParent(); 95 | if (!(template instanceof JtePsiTemplate)) { 96 | return; 97 | } 98 | 99 | JtePsiTemplateName firstTemplateName = PsiTreeUtil.getChildOfType(template, JtePsiTemplateName.class); 100 | JtePsiTemplateName lastTemplateName = JtePsiUtil.getLastChildOfType(template, JtePsiTemplateName.class); 101 | if (firstTemplateName == null || lastTemplateName == null) { 102 | return; 103 | } 104 | 105 | String newTemplateLocation = getNewTemplateLocation(newFile, rootDirectory); 106 | 107 | JtePsiTemplate tempTemplate = createNewDummyTemplateNode(template, newTemplateLocation); 108 | JtePsiTemplateName firstTempTemplateName = PsiTreeUtil.getChildOfType(tempTemplate, JtePsiTemplateName.class); 109 | JtePsiTemplateName lastTempTemplateName = JtePsiUtil.getLastChildOfType(tempTemplate, JtePsiTemplateName.class); 110 | 111 | if (firstTempTemplateName != null && lastTempTemplateName != null) { 112 | template.addRangeBefore(firstTempTemplateName, lastTempTemplateName, firstTemplateName); 113 | template.deleteChildRange(firstTemplateName, lastTemplateName); 114 | } 115 | } 116 | 117 | @Nullable 118 | private JtePsiTemplate createNewDummyTemplateNode(PsiElement template, String newTemplateLocation) { 119 | String text = "@template." + newTemplateLocation + "()"; 120 | Project project = template.getProject(); 121 | PsiManager psiManager = PsiManager.getInstance(project); 122 | DummyHolder dummyHolder = DummyHolderFactory.createHolder(psiManager, null); 123 | ParserDefinition parserDefinition = LanguageParserDefinitions.INSTANCE.forLanguage(template.getLanguage()); 124 | Lexer lexer = parserDefinition.createLexer(project); 125 | PsiBuilder psiBuilder = PsiBuilderFactory.getInstance().createBuilder(project, dummyHolder.getTreeElement(), lexer, template.getLanguage(), text); 126 | ASTNode node = parserDefinition.createParser(project).parse(JteTokenTypes.HTML_CONTENT, psiBuilder); 127 | 128 | dummyHolder.getTreeElement().rawAddChildren((TreeElement) node); 129 | 130 | CodeEditUtil.setNodeGeneratedRecursively(node, true); 131 | 132 | return PsiTreeUtil.findChildOfType(dummyHolder, JtePsiTemplate.class); 133 | } 134 | 135 | @NotNull 136 | private String getNewTemplateLocation(PsiFile newFile, PsiDirectory rootDirectory) { 137 | List names = new ArrayList<>(); 138 | 139 | names.add(newFile.getVirtualFile().getNameWithoutExtension()); 140 | 141 | PsiDirectory parent = newFile.getParent(); 142 | while (parent != null && !parent.equals(rootDirectory)) { 143 | names.add(parent.getName()); 144 | parent = parent.getParent(); 145 | } 146 | 147 | Collections.reverse(names); 148 | return String.join(".", names); 149 | } 150 | 151 | @Override 152 | public void updateMovedFile(PsiFile file) throws IncorrectOperationException { 153 | // Nothing to do 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/psi/JtePsiUtil.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.psi; 2 | 3 | import java.util.*; 4 | 5 | import com.intellij.lang.injection.InjectedLanguageManager; 6 | import com.intellij.psi.*; 7 | import com.intellij.psi.impl.source.tree.LeafPsiElement; 8 | import com.intellij.psi.tree.IElementType; 9 | import com.intellij.psi.util.PsiTreeUtil; 10 | import com.intellij.util.IncorrectOperationException; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | import org.jusecase.jte.intellij.language.JteLanguage; 14 | 15 | 16 | public class JtePsiUtil { 17 | public static void rename(JtePsiElement element, String name) throws IncorrectOperationException { 18 | LeafPsiElement leaf = PsiTreeUtil.getChildOfType(element, LeafPsiElement.class); 19 | if (leaf == null) { 20 | throw new IncorrectOperationException("Could not rename, no leaf found!"); 21 | } 22 | 23 | leaf.replaceWithText(name); 24 | } 25 | 26 | @NotNull 27 | public static T getFirstSiblingOfSameType(T element, Class clazz) { 28 | T sibling = PsiTreeUtil.getPrevSiblingOfType(element, clazz); 29 | if (sibling == null) { 30 | return element; 31 | } else { 32 | return getFirstSiblingOfSameType(sibling, clazz); 33 | } 34 | } 35 | 36 | @Nullable 37 | public static T getFirstSiblingOfType(PsiElement element, Class clazz) { 38 | T result = null; 39 | T sibling = PsiTreeUtil.getPrevSiblingOfType(element, clazz); 40 | while (sibling != null) { 41 | result = sibling; 42 | sibling = PsiTreeUtil.getPrevSiblingOfType(sibling, clazz); 43 | } 44 | 45 | return result; 46 | } 47 | 48 | @Nullable 49 | public static PsiElement getPrevSiblingIgnoring(PsiElement element, IElementType elementType) { 50 | for (PsiElement child = element.getPrevSibling(); child != null; child = child.getPrevSibling()) { 51 | if (child.getNode().getElementType() != elementType) { 52 | return child; 53 | } 54 | } 55 | return null; 56 | } 57 | 58 | public static Set resolveAvailableParameterNames(PsiFile templateFile) { 59 | PsiParameterList psiParameterList = resolveParameterList(templateFile); 60 | if (psiParameterList == null || psiParameterList.getParametersCount() == 0) { 61 | return Collections.emptySet(); 62 | } 63 | 64 | Set result = new LinkedHashSet<>(psiParameterList.getParametersCount()); 65 | for (PsiParameter parameter : psiParameterList.getParameters()) { 66 | result.add(parameter.getName()); 67 | } 68 | 69 | return result; 70 | } 71 | 72 | public static PsiParameterList resolveParameterList(PsiFile templateFile) { 73 | PsiFile jteFile = templateFile.getViewProvider().getPsi(JteLanguage.INSTANCE); 74 | if (jteFile == null) { 75 | return null; 76 | } 77 | 78 | JtePsiParam param = PsiTreeUtil.findChildOfType(jteFile, JtePsiParam.class); 79 | if (param == null) { 80 | return null; // Template has no parameters 81 | } 82 | 83 | JtePsiJavaInjection javaInjection = PsiTreeUtil.findChildOfType(param, JtePsiJavaInjection.class); 84 | if (javaInjection == null) { 85 | return null; 86 | } 87 | 88 | PsiElement injectedElementAt = InjectedLanguageManager.getInstance(javaInjection.getProject()).findInjectedElementAt(javaInjection.getContainingFile(), javaInjection.getTextOffset()); 89 | 90 | return PsiTreeUtil.getParentOfType(injectedElementAt, PsiParameterList.class); 91 | } 92 | 93 | public static List resolveRequiredParameters(PsiFile templateFile) { 94 | PsiFile jteFile = templateFile.getViewProvider().getPsi(JteLanguage.INSTANCE); 95 | if (jteFile == null) { 96 | return Collections.emptyList(); 97 | } 98 | 99 | Collection params = PsiTreeUtil.findChildrenOfType(jteFile, JtePsiParam.class); 100 | if (params.isEmpty()) { 101 | return Collections.emptyList(); 102 | } 103 | 104 | PsiParameterList psiParameterList = resolveParameterList(templateFile); 105 | if (psiParameterList == null) { 106 | return Collections.emptyList(); 107 | } 108 | 109 | if (params.size() != psiParameterList.getParametersCount()) { 110 | return Collections.emptyList(); 111 | } 112 | 113 | List result = new ArrayList<>(); 114 | int i = 0; 115 | for ( JtePsiParam param : params ) { 116 | PsiParameter psiParam = psiParameterList.getParameter(i); 117 | if (psiParam != null) { 118 | // Hack: Optional parameters have an additional JtePsiExtraJavaInjection node... 119 | if (PsiTreeUtil.getChildOfType(param, JtePsiExtraJavaInjection.class) == null) { 120 | result.add(psiParam); // ... so, this is a required parameter 121 | } 122 | } 123 | i++; 124 | } 125 | 126 | return result; 127 | } 128 | 129 | @SuppressWarnings("unused") 130 | public static T getLastChildOfType(PsiElement element, Class clazz) { 131 | T result = PsiTreeUtil.getChildOfType(element, clazz); 132 | if (result == null) { 133 | return null; 134 | } 135 | 136 | T sibling = result; 137 | while (sibling != null) { 138 | result = sibling; 139 | sibling = PsiTreeUtil.getNextSiblingOfType(result, clazz); 140 | } 141 | 142 | return result; 143 | } 144 | 145 | public static T getTopMostParentOfType(@Nullable PsiElement element, @NotNull Class aClass, int minStartOffset) { 146 | if (element == null) { 147 | return null; 148 | } 149 | 150 | T result = null; 151 | 152 | while (element != null && (minStartOffset == -1 || element.getNode().getStartOffset() >= minStartOffset)) { 153 | if (aClass.isInstance(element)) { 154 | result = aClass.cast(element); 155 | } else if (result != null) { 156 | break; 157 | } 158 | if (element instanceof PsiFile) { 159 | break; 160 | } 161 | element = element.getParent(); 162 | } 163 | 164 | return result; 165 | } 166 | 167 | public static T getNextSiblingIfBefore(PsiElement element, Class classToFind, Class classToStopAt) { 168 | for (element = element.getNextSibling(); element != null; element = element.getNextSibling()) { 169 | if (classToFind.isAssignableFrom(element.getClass())) { 170 | //noinspection unchecked 171 | return (T)element; 172 | } 173 | 174 | if (classToStopAt.isAssignableFrom(element.getClass())) { 175 | return null; 176 | } 177 | } 178 | 179 | return null; 180 | } 181 | 182 | public static T getPrevSiblingIfBefore(PsiElement element, Class classToFind, Class classToStopAt) { 183 | for (element = element.getPrevSibling(); element != null; element = element.getPrevSibling()) { 184 | if (classToFind.isAssignableFrom(element.getClass())) { 185 | //noinspection unchecked 186 | return (T)element; 187 | } 188 | 189 | if (classToStopAt.isAssignableFrom(element.getClass())) { 190 | return null; 191 | } 192 | } 193 | 194 | return null; 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/JteTokenTypes.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing; 2 | 3 | import com.intellij.psi.tree.*; 4 | import org.jusecase.jte.intellij.language.JteLanguage; 5 | 6 | public class JteTokenTypes implements TokenTypes { 7 | public static final JteTokenTypes INSTANCE = new JteTokenTypes(); 8 | 9 | public static final IElementType HTML_CONTENT = new JteElementType("HTML_CONTENT"); 10 | public static final IElementType JAVA_CONTENT = new JteElementType("JAVA_CONTENT"); 11 | public static final IElementType BLOCK = new JteElementType("BLOCK"); 12 | 13 | public static final IElementType JAVA_INJECTION = new JteElementType("JAVA_INJECTION"); 14 | public static final IElementType EXTRA_JAVA_INJECTION = new JteElementType("EXTRA_JAVA_INJECTION"); 15 | 16 | public static final IElementType OUTER_ELEMENT_TYPE = new OuterLanguageElementType("OUTER_ELEMENT_TYPE", JteLanguage.INSTANCE); 17 | 18 | public static final IElementType IMPORT = new JteElementType("IMPORT"); 19 | public static final IElementType PARAM = new JteElementType("PARAM"); 20 | public static final IElementType OUTPUT = new JteElementType("OUTPUT"); 21 | public static final IElementType OUTPUT_BEGIN = new JteElementType("OUTPUT_BEGIN"); 22 | public static final IElementType OUTPUT_END = new JteElementType("OUTPUT_END"); 23 | public static final IElementType STATEMENT = new JteElementType("STATEMENT"); 24 | public static final IElementType STATEMENT_BEGIN = new JteElementType("STATEMENT_BEGIN"); 25 | public static final IElementType STATEMENT_END = new JteElementType("STATEMENT_END"); 26 | 27 | public static final IElementType IF = new JteElementType("IF"); 28 | public static final IElementType CONDITION_BEGIN = new JteElementType("CONDITION_BEGIN"); 29 | public static final IElementType CONDITION_END = new JteElementType("CONDITION_END"); 30 | public static final IElementType ENDIF = new JteElementType("ENDIF"); 31 | public static final IElementType ELSE = new JteElementType("ELSE"); 32 | public static final IElementType ELSEIF = new JteElementType("ELSEIF"); 33 | public static final IElementType FOR = new JteElementType("FOR"); 34 | public static final IElementType ENDFOR = new JteElementType("ENDFOR"); 35 | public static final IElementType RAW = new JteElementType("RAW"); 36 | public static final IElementType ENDRAW = new JteElementType("ENDRAW"); 37 | 38 | public static final IElementType TEMPLATE = new JteElementType("TEMPLATE"); 39 | public static final IElementType TEMPLATE_NAME = new JteElementType("TEMPLATE_NAME"); 40 | public static final IElementType NAME_SEPARATOR = new JteElementType("NAME_SEPARATOR"); 41 | public static final IElementType PARAMS_BEGIN = new JteElementType("PARAMS_BEGIN"); 42 | public static final IElementType PARAM_NAME = new JteElementType("PARAM_NAME"); 43 | public static final IElementType PARAMS_END = new JteElementType("PARAMS_END"); 44 | public static final IElementType CONTENT_BEGIN = new JteElementType("CONTENT_BEGIN"); 45 | public static final IElementType CONTENT_END = new JteElementType("CONTENT_END"); 46 | 47 | public static final IElementType COMMENT = new JteElementType("COMMENT"); 48 | public static final IElementType COMMENT_CONTENT = new JteElementType("COMMENT_CONTENT"); 49 | 50 | public static final IElementType WHITESPACE = new JteElementType("WHITESPACE"); 51 | public static final IElementType EQUALS = new JteElementType("EQUALS"); 52 | public static final IElementType COMMA = new JteElementType("COMMA"); 53 | 54 | public static final IElementType STRING = new JteElementType("STRING"); 55 | 56 | public static final IFileElementType FILE = new IStubFileElementType<>("JTE_FILE", JteLanguage.INSTANCE); 57 | 58 | public static final TokenSet COMMENTS = TokenSet.create(COMMENT, COMMENT_CONTENT); 59 | public static final TokenSet STRING_LITERALS = TokenSet.create(STRING); 60 | public static final TokenSet WHITESPACES = TokenSet.create(WHITESPACE); 61 | 62 | 63 | @Override 64 | public IElementType HTML_CONTENT() { 65 | return HTML_CONTENT; 66 | } 67 | 68 | @Override 69 | public IElementType JAVA_CONTENT() { 70 | return JAVA_CONTENT; 71 | } 72 | 73 | @Override 74 | public IElementType BLOCK() { 75 | return BLOCK; 76 | } 77 | 78 | @Override 79 | public IElementType JAVA_INJECTION() { 80 | return JAVA_INJECTION; 81 | } 82 | 83 | @Override 84 | public IElementType EXTRA_JAVA_INJECTION() { 85 | return EXTRA_JAVA_INJECTION; 86 | } 87 | 88 | @Override 89 | public IElementType OUTER_ELEMENT_TYPE() { 90 | return OUTER_ELEMENT_TYPE; 91 | } 92 | 93 | @Override 94 | public IElementType IMPORT() { 95 | return IMPORT; 96 | } 97 | 98 | @Override 99 | public IElementType PARAM() { 100 | return PARAM; 101 | } 102 | 103 | @Override 104 | public IElementType OUTPUT() { 105 | return OUTPUT; 106 | } 107 | 108 | @Override 109 | public IElementType OUTPUT_BEGIN() { 110 | return OUTPUT_BEGIN; 111 | } 112 | 113 | @Override 114 | public IElementType OUTPUT_END() { 115 | return OUTPUT_END; 116 | } 117 | 118 | @Override 119 | public IElementType STATEMENT() { 120 | return STATEMENT; 121 | } 122 | 123 | @Override 124 | public IElementType STATEMENT_BEGIN() { 125 | return STATEMENT_BEGIN; 126 | } 127 | 128 | @Override 129 | public IElementType STATEMENT_END() { 130 | return STATEMENT_END; 131 | } 132 | 133 | @Override 134 | public IElementType IF() { 135 | return IF; 136 | } 137 | 138 | @Override 139 | public IElementType CONDITION_BEGIN() { 140 | return CONDITION_BEGIN; 141 | } 142 | 143 | @Override 144 | public IElementType CONDITION_END() { 145 | return CONDITION_END; 146 | } 147 | 148 | @Override 149 | public IElementType ENDIF() { 150 | return ENDIF; 151 | } 152 | 153 | @Override 154 | public IElementType ELSE() { 155 | return ELSE; 156 | } 157 | 158 | @Override 159 | public IElementType ELSEIF() { 160 | return ELSEIF; 161 | } 162 | 163 | @Override 164 | public IElementType FOR() { 165 | return FOR; 166 | } 167 | 168 | @Override 169 | public IElementType ENDFOR() { 170 | return ENDFOR; 171 | } 172 | 173 | @Override 174 | public IElementType RAW() { 175 | return RAW; 176 | } 177 | 178 | @Override 179 | public IElementType ENDRAW() { 180 | return ENDRAW; 181 | } 182 | 183 | @Override 184 | public IElementType TEMPLATE() { 185 | return TEMPLATE; 186 | } 187 | 188 | @Override 189 | public IElementType TEMPLATE_NAME() { 190 | return TEMPLATE_NAME; 191 | } 192 | 193 | @Override 194 | public IElementType NAME_SEPARATOR() { 195 | return NAME_SEPARATOR; 196 | } 197 | 198 | @Override 199 | public IElementType PARAMS_BEGIN() { 200 | return PARAMS_BEGIN; 201 | } 202 | 203 | @Override 204 | public IElementType PARAM_NAME() { 205 | return PARAM_NAME; 206 | } 207 | 208 | @Override 209 | public IElementType PARAMS_END() { 210 | return PARAMS_END; 211 | } 212 | 213 | @Override 214 | public IElementType CONTENT_BEGIN() { 215 | return CONTENT_BEGIN; 216 | } 217 | 218 | @Override 219 | public IElementType CONTENT_END() { 220 | return CONTENT_END; 221 | } 222 | 223 | @Override 224 | public IElementType COMMENT() { 225 | return COMMENT; 226 | } 227 | 228 | @Override 229 | public IElementType COMMENT_CONTENT() { 230 | return COMMENT_CONTENT; 231 | } 232 | 233 | @Override 234 | public IElementType WHITESPACE() { 235 | return WHITESPACE; 236 | } 237 | 238 | @Override 239 | public IElementType EQUALS() { 240 | return EQUALS; 241 | } 242 | 243 | @Override 244 | public IElementType COMMA() { 245 | return COMMA; 246 | } 247 | 248 | @Override 249 | public IElementType STRING() { 250 | return STRING; 251 | } 252 | 253 | @Override 254 | public IFileElementType FILE() { 255 | return FILE; 256 | } 257 | 258 | @Override 259 | public TokenSet COMMENTS() { 260 | return COMMENTS; 261 | } 262 | 263 | @Override 264 | public TokenSet STRING_LITERALS() { 265 | return STRING_LITERALS; 266 | } 267 | 268 | @Override 269 | public TokenSet WHITESPACES() { 270 | return WHITESPACES; 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /src/main/java/org/jusecase/jte/intellij/language/parsing/KteTokenTypes.java: -------------------------------------------------------------------------------- 1 | package org.jusecase.jte.intellij.language.parsing; 2 | 3 | import com.intellij.psi.tree.*; 4 | import org.jusecase.jte.intellij.language.KteLanguage; 5 | 6 | public class KteTokenTypes implements TokenTypes { 7 | public static final KteTokenTypes INSTANCE = new KteTokenTypes(); 8 | 9 | public static final IElementType HTML_CONTENT = new KteElementType("HTML_CONTENT"); 10 | public static final IElementType JAVA_CONTENT = new KteElementType("JAVA_CONTENT"); 11 | public static final IElementType BLOCK = new KteElementType("BLOCK"); 12 | 13 | public static final IElementType JAVA_INJECTION = new KteElementType("JAVA_INJECTION"); 14 | public static final IElementType EXTRA_JAVA_INJECTION = new KteElementType("EXTRA_JAVA_INJECTION"); 15 | 16 | public static final IElementType OUTER_ELEMENT_TYPE = new OuterLanguageElementType("OUTER_ELEMENT_TYPE", KteLanguage.INSTANCE); 17 | 18 | public static final IElementType IMPORT = new KteElementType("IMPORT"); 19 | public static final IElementType PARAM = new KteElementType("PARAM"); 20 | public static final IElementType OUTPUT = new KteElementType("OUTPUT"); 21 | public static final IElementType OUTPUT_BEGIN = new KteElementType("OUTPUT_BEGIN"); 22 | public static final IElementType OUTPUT_END = new KteElementType("OUTPUT_END"); 23 | public static final IElementType STATEMENT = new KteElementType("STATEMENT"); 24 | public static final IElementType STATEMENT_BEGIN = new KteElementType("STATEMENT_BEGIN"); 25 | public static final IElementType STATEMENT_END = new KteElementType("STATEMENT_END"); 26 | 27 | public static final IElementType IF = new KteElementType("IF"); 28 | public static final IElementType CONDITION_BEGIN = new KteElementType("CONDITION_BEGIN"); 29 | public static final IElementType CONDITION_END = new KteElementType("CONDITION_END"); 30 | public static final IElementType ENDIF = new KteElementType("ENDIF"); 31 | public static final IElementType ELSE = new KteElementType("ELSE"); 32 | public static final IElementType ELSEIF = new KteElementType("ELSEIF"); 33 | public static final IElementType FOR = new KteElementType("FOR"); 34 | public static final IElementType ENDFOR = new KteElementType("ENDFOR"); 35 | public static final IElementType RAW = new KteElementType("RAW"); 36 | public static final IElementType ENDRAW = new KteElementType("ENDRAW"); 37 | 38 | public static final IElementType TEMPLATE = new KteElementType("TEMPLATE"); 39 | public static final IElementType TEMPLATE_NAME = new KteElementType("TEMPLATE_NAME"); 40 | public static final IElementType NAME_SEPARATOR = new KteElementType("NAME_SEPARATOR"); 41 | public static final IElementType PARAMS_BEGIN = new KteElementType("PARAMS_BEGIN"); 42 | public static final IElementType PARAM_NAME = new KteElementType("PARAM_NAME"); 43 | public static final IElementType PARAMS_END = new KteElementType("PARAMS_END"); 44 | public static final IElementType CONTENT_BEGIN = new KteElementType("CONTENT_BEGIN"); 45 | public static final IElementType CONTENT_END = new KteElementType("CONTENT_END"); 46 | 47 | public static final IElementType COMMENT = new KteElementType("COMMENT"); 48 | public static final IElementType COMMENT_CONTENT = new KteElementType("COMMENT_CONTENT"); 49 | 50 | public static final IElementType WHITESPACE = new KteElementType("WHITESPACE"); 51 | public static final IElementType EQUALS = new KteElementType("EQUALS"); 52 | public static final IElementType COMMA = new KteElementType("COMMA"); 53 | 54 | public static final IElementType STRING = new KteElementType("STRING"); 55 | 56 | public static final IFileElementType FILE = new IStubFileElementType<>("KTE_FILE", KteLanguage.INSTANCE); 57 | 58 | public static final TokenSet COMMENTS = TokenSet.create(COMMENT, COMMENT_CONTENT); 59 | public static final TokenSet STRING_LITERALS = TokenSet.create(STRING); 60 | public static final TokenSet WHITESPACES = TokenSet.create(WHITESPACE); 61 | 62 | 63 | @Override 64 | public IElementType HTML_CONTENT() { 65 | return HTML_CONTENT; 66 | } 67 | 68 | @Override 69 | public IElementType JAVA_CONTENT() { 70 | return JAVA_CONTENT; 71 | } 72 | 73 | @Override 74 | public IElementType BLOCK() { 75 | return BLOCK; 76 | } 77 | 78 | @Override 79 | public IElementType JAVA_INJECTION() { 80 | return JAVA_INJECTION; 81 | } 82 | 83 | @Override 84 | public IElementType EXTRA_JAVA_INJECTION() { 85 | return EXTRA_JAVA_INJECTION; 86 | } 87 | 88 | @Override 89 | public IElementType OUTER_ELEMENT_TYPE() { 90 | return OUTER_ELEMENT_TYPE; 91 | } 92 | 93 | @Override 94 | public IElementType IMPORT() { 95 | return IMPORT; 96 | } 97 | 98 | @Override 99 | public IElementType PARAM() { 100 | return PARAM; 101 | } 102 | 103 | @Override 104 | public IElementType OUTPUT() { 105 | return OUTPUT; 106 | } 107 | 108 | @Override 109 | public IElementType OUTPUT_BEGIN() { 110 | return OUTPUT_BEGIN; 111 | } 112 | 113 | @Override 114 | public IElementType OUTPUT_END() { 115 | return OUTPUT_END; 116 | } 117 | 118 | @Override 119 | public IElementType STATEMENT() { 120 | return STATEMENT; 121 | } 122 | 123 | @Override 124 | public IElementType STATEMENT_BEGIN() { 125 | return STATEMENT_BEGIN; 126 | } 127 | 128 | @Override 129 | public IElementType STATEMENT_END() { 130 | return STATEMENT_END; 131 | } 132 | 133 | @Override 134 | public IElementType IF() { 135 | return IF; 136 | } 137 | 138 | @Override 139 | public IElementType CONDITION_BEGIN() { 140 | return CONDITION_BEGIN; 141 | } 142 | 143 | @Override 144 | public IElementType CONDITION_END() { 145 | return CONDITION_END; 146 | } 147 | 148 | @Override 149 | public IElementType ENDIF() { 150 | return ENDIF; 151 | } 152 | 153 | @Override 154 | public IElementType ELSE() { 155 | return ELSE; 156 | } 157 | 158 | @Override 159 | public IElementType ELSEIF() { 160 | return ELSEIF; 161 | } 162 | 163 | @Override 164 | public IElementType FOR() { 165 | return FOR; 166 | } 167 | 168 | @Override 169 | public IElementType ENDFOR() { 170 | return ENDFOR; 171 | } 172 | 173 | @Override 174 | public IElementType RAW() { 175 | return RAW; 176 | } 177 | 178 | @Override 179 | public IElementType ENDRAW() { 180 | return ENDRAW; 181 | } 182 | 183 | @Override 184 | public IElementType TEMPLATE() { 185 | return TEMPLATE; 186 | } 187 | 188 | @Override 189 | public IElementType TEMPLATE_NAME() { 190 | return TEMPLATE_NAME; 191 | } 192 | 193 | @Override 194 | public IElementType NAME_SEPARATOR() { 195 | return NAME_SEPARATOR; 196 | } 197 | 198 | @Override 199 | public IElementType PARAMS_BEGIN() { 200 | return PARAMS_BEGIN; 201 | } 202 | 203 | @Override 204 | public IElementType PARAM_NAME() { 205 | return PARAM_NAME; 206 | } 207 | 208 | @Override 209 | public IElementType PARAMS_END() { 210 | return PARAMS_END; 211 | } 212 | 213 | @Override 214 | public IElementType CONTENT_BEGIN() { 215 | return CONTENT_BEGIN; 216 | } 217 | 218 | @Override 219 | public IElementType CONTENT_END() { 220 | return CONTENT_END; 221 | } 222 | 223 | @Override 224 | public IElementType COMMENT() { 225 | return COMMENT; 226 | } 227 | 228 | @Override 229 | public IElementType COMMENT_CONTENT() { 230 | return COMMENT_CONTENT; 231 | } 232 | 233 | @Override 234 | public IElementType WHITESPACE() { 235 | return WHITESPACE; 236 | } 237 | 238 | @Override 239 | public IElementType EQUALS() { 240 | return EQUALS; 241 | } 242 | 243 | @Override 244 | public IElementType COMMA() { 245 | return COMMA; 246 | } 247 | 248 | @Override 249 | public IElementType STRING() { 250 | return STRING; 251 | } 252 | 253 | @Override 254 | public IFileElementType FILE() { 255 | return FILE; 256 | } 257 | 258 | @Override 259 | public TokenSet COMMENTS() { 260 | return COMMENTS; 261 | } 262 | 263 | @Override 264 | public TokenSet STRING_LITERALS() { 265 | return STRING_LITERALS; 266 | } 267 | 268 | @Override 269 | public TokenSet WHITESPACES() { 270 | return WHITESPACES; 271 | } 272 | } 273 | --------------------------------------------------------------------------------