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 extends PsiLanguageInjectionHost> 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 extends PsiLanguageInjectionHost> 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 extends PsiLanguageInjectionHost> 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 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
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 |
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 extends PsiElement> 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 extends PsiElement> 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 |
--------------------------------------------------------------------------------