├── images ├── editor.png ├── java-highlight.png └── template-data-language.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src ├── main │ ├── resources │ │ ├── icons │ │ │ ├── dict.png │ │ │ ├── st.png │ │ │ ├── st@2x.png │ │ │ ├── string.png │ │ │ ├── bigstring.png │ │ │ ├── dict@2x.png │ │ │ ├── string@2x.png │ │ │ ├── bigstring-nonl.png │ │ │ ├── bigstring@2x.png │ │ │ ├── bigstring-nonl@2x.png │ │ │ ├── string.svg │ │ │ ├── dict.svg │ │ │ ├── bigstring.svg │ │ │ └── bigstring-nonl.svg │ │ ├── META-INF │ │ │ ├── st-intellilang.xml │ │ │ ├── pluginIcon.svg │ │ │ └── plugin.xml │ │ ├── colorSchemes │ │ │ ├── STGroupDarcula.xml │ │ │ └── STGroupDefault.xml │ │ └── st-java-injections.xml │ ├── java │ │ └── org │ │ │ └── antlr │ │ │ └── jetbrains │ │ │ └── st4plugin │ │ │ ├── STLanguage.java │ │ │ ├── STGroupLanguage.java │ │ │ ├── structview │ │ │ ├── STGroupStructureViewFactory.java │ │ │ ├── STGroupStructureViewBuilder.java │ │ │ ├── STGroupRootItemPresentation.java │ │ │ ├── STGroupItemPresentation.java │ │ │ ├── STGroupTemplateDefTreeElement.java │ │ │ ├── STGroupStructureViewModel.java │ │ │ ├── STGroupRootTreeElement.java │ │ │ ├── STGroupStructureViewTreeElement.java │ │ │ └── STGroupTemplateDefItemPresentation.java │ │ │ ├── Icons.java │ │ │ ├── highlight │ │ │ ├── STSyntaxHighlighterFactory.java │ │ │ ├── STGroupSyntaxHighlighterFactory.java │ │ │ ├── STGroupBraceMatcher.java │ │ │ ├── STBraceMatcher.java │ │ │ ├── STGroupSemanticHighlightAnnotator.java │ │ │ ├── STEditorHighlighterProvider.java │ │ │ ├── STSyntaxHighlighter.java │ │ │ ├── STGroupSyntaxHighlighter.java │ │ │ ├── STColorSettingsPage.java │ │ │ └── STSemanticHighlightAnnotator.java │ │ │ ├── psi │ │ │ ├── TemplateContentElement.java │ │ │ ├── STFile.java │ │ │ ├── STGroupFile.java │ │ │ ├── STTokenTypes.java │ │ │ ├── STGroupTokenTypes.java │ │ │ ├── TemplateContentLiteralTextEscaper.java │ │ │ ├── STLanguageInjector.java │ │ │ ├── STGroupParserDefinition.java │ │ │ └── STParserDefinition.java │ │ │ ├── STFileType.java │ │ │ ├── STGroupFileType.java │ │ │ ├── folding │ │ │ ├── STFoldingBuilder.java │ │ │ └── STGroupFoldingBuilder.java │ │ │ └── parsing │ │ │ └── LexerAdaptor.java │ └── antlr │ │ ├── STGParser.g4 │ │ ├── STGLexer.g4 │ │ ├── STLexer.g4 │ │ ├── STParser.g4 │ │ ├── LexBasic.g4 │ │ └── LexUnicode.g4 └── test │ └── java │ └── org │ └── antlr │ └── jetbrains │ └── st4plugin │ ├── psi │ └── TemplateContentLiteralTextEscaperTest.java │ └── structview │ └── STGroupTemplateDefItemPresentationTest.java ├── .gitignore ├── gradle.properties ├── .editorconfig ├── LICENSE ├── .github └── workflows │ └── gradle.yml ├── README.md ├── contributors.txt ├── gradlew.bat └── gradlew /images/editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/jetbrains-plugin-st4/HEAD/images/editor.png -------------------------------------------------------------------------------- /images/java-highlight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/jetbrains-plugin-st4/HEAD/images/java-highlight.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/jetbrains-plugin-st4/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /images/template-data-language.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/jetbrains-plugin-st4/HEAD/images/template-data-language.png -------------------------------------------------------------------------------- /src/main/resources/icons/dict.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/jetbrains-plugin-st4/HEAD/src/main/resources/icons/dict.png -------------------------------------------------------------------------------- /src/main/resources/icons/st.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/jetbrains-plugin-st4/HEAD/src/main/resources/icons/st.png -------------------------------------------------------------------------------- /src/main/resources/icons/st@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/jetbrains-plugin-st4/HEAD/src/main/resources/icons/st@2x.png -------------------------------------------------------------------------------- /src/main/resources/icons/string.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/jetbrains-plugin-st4/HEAD/src/main/resources/icons/string.png -------------------------------------------------------------------------------- /src/main/resources/icons/bigstring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/jetbrains-plugin-st4/HEAD/src/main/resources/icons/bigstring.png -------------------------------------------------------------------------------- /src/main/resources/icons/dict@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/jetbrains-plugin-st4/HEAD/src/main/resources/icons/dict@2x.png -------------------------------------------------------------------------------- /src/main/resources/icons/string@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/jetbrains-plugin-st4/HEAD/src/main/resources/icons/string@2x.png -------------------------------------------------------------------------------- /src/main/resources/icons/bigstring-nonl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/jetbrains-plugin-st4/HEAD/src/main/resources/icons/bigstring-nonl.png -------------------------------------------------------------------------------- /src/main/resources/icons/bigstring@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/jetbrains-plugin-st4/HEAD/src/main/resources/icons/bigstring@2x.png -------------------------------------------------------------------------------- /src/main/resources/icons/bigstring-nonl@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antlr/jetbrains-plugin-st4/HEAD/src/main/resources/icons/bigstring-nonl@2x.png -------------------------------------------------------------------------------- /src/main/resources/META-INF/st-intellilang.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | 8 | *.war 9 | *.ear 10 | 11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 12 | hs_err_pid* 13 | 14 | /build 15 | /bin 16 | /.gradle 17 | /.idea/ 18 | *.iml 19 | *.ipr 20 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | pluginVersion=0.10 2 | 3 | # e.g. IC-2016.3.3, IU-2018.2.5 etc 4 | # For a list of possible values, refer to the section 'com.jetbrains.intellij.idea' at https://www.jetbrains.com/intellij-repository/releases 5 | ideaVersion=2022.2.5 6 | 7 | # The version of ANTLR v4 that will be used to generate the parser 8 | antlr4Version=4.13.1 9 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/STLanguage.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin; 2 | 3 | import com.intellij.lang.Language; 4 | 5 | public class STLanguage extends Language { 6 | 7 | public static final STLanguage INSTANCE = new STLanguage(); 8 | 9 | private STLanguage() { 10 | super("ST"); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/resources/colorSchemes/STGroupDarcula.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 13 | -------------------------------------------------------------------------------- /src/main/resources/colorSchemes/STGroupDefault.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 13 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/STGroupLanguage.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin; 2 | 3 | import com.intellij.lang.Language; 4 | 5 | public class STGroupLanguage extends Language { 6 | public static final STGroupLanguage INSTANCE = new STGroupLanguage(); 7 | 8 | private STGroupLanguage() { 9 | super("STGroup"); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/resources/icons/string.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 4 7 | indent_style = space 8 | insert_final_newline = true 9 | max_line_length = 240 10 | tab_width = 4 11 | trim_trailing_whitespace = true 12 | ij_continuation_indent_size = 8 13 | ij_formatter_off_tag = @formatter:off 14 | ij_formatter_on_tag = @formatter:on 15 | ij_formatter_tags_enabled = true 16 | ij_smart_tabs = false 17 | ij_wrap_on_typing = false 18 | 19 | ij_java_class_count_to_use_import_on_demand = 999 20 | -------------------------------------------------------------------------------- /src/main/resources/icons/dict.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/main/resources/icons/bigstring.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/structview/STGroupStructureViewFactory.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.structview; 2 | 3 | import com.intellij.ide.structureView.StructureViewBuilder; 4 | import com.intellij.lang.PsiStructureViewFactory; 5 | import com.intellij.psi.PsiFile; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | public class STGroupStructureViewFactory implements PsiStructureViewFactory { 10 | @Nullable 11 | @Override 12 | public StructureViewBuilder getStructureViewBuilder(@NotNull PsiFile psiFile) { 13 | return new STGroupStructureViewBuilder(psiFile); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/Icons.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin; 2 | 3 | import com.intellij.openapi.util.IconLoader; 4 | 5 | import javax.swing.*; 6 | 7 | public final class Icons { 8 | public static final Icon STG_FILE = IconLoader.getIcon("/icons/st.png", Icons.class); 9 | public static final Icon BIGSTRING = IconLoader.getIcon("/icons/bigstring.png", Icons.class); 10 | public static final Icon BIGSTRING_NONL = IconLoader.getIcon("/icons/bigstring-nonl.png", Icons.class); 11 | public static final Icon STRING = IconLoader.getIcon("/icons/string.png", Icons.class); 12 | public static final Icon DICT = IconLoader.getIcon("/icons/dict.png", Icons.class); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/highlight/STSyntaxHighlighterFactory.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.highlight; 2 | 3 | import com.intellij.openapi.fileTypes.SyntaxHighlighter; 4 | import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.vfs.VirtualFile; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | public class STSyntaxHighlighterFactory extends SyntaxHighlighterFactory { 11 | @NotNull 12 | @Override 13 | public SyntaxHighlighter getSyntaxHighlighter(@Nullable Project project, @Nullable VirtualFile virtualFile) { 14 | return new STSyntaxHighlighter(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/highlight/STGroupSyntaxHighlighterFactory.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.highlight; 2 | 3 | import com.intellij.openapi.fileTypes.SyntaxHighlighter; 4 | import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.vfs.VirtualFile; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | public class STGroupSyntaxHighlighterFactory extends SyntaxHighlighterFactory { 11 | 12 | @NotNull 13 | @Override 14 | public SyntaxHighlighter getSyntaxHighlighter(@Nullable Project project, @Nullable VirtualFile virtualFile) { 15 | return new STGroupSyntaxHighlighter(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/structview/STGroupStructureViewBuilder.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.structview; 2 | 3 | import com.intellij.ide.structureView.StructureViewModel; 4 | import com.intellij.ide.structureView.TreeBasedStructureViewBuilder; 5 | import com.intellij.openapi.editor.Editor; 6 | import com.intellij.psi.PsiFile; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public class STGroupStructureViewBuilder extends TreeBasedStructureViewBuilder { 10 | 11 | private final PsiFile psiFile; 12 | 13 | public STGroupStructureViewBuilder(PsiFile psiFile) { 14 | this.psiFile = psiFile; 15 | } 16 | 17 | @NotNull 18 | @Override 19 | public StructureViewModel createStructureViewModel(final Editor editor) { 20 | return new STGroupStructureViewModel(psiFile); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/pluginIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/structview/STGroupRootItemPresentation.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.structview; 2 | 3 | import com.intellij.navigation.ItemPresentation; 4 | import com.intellij.psi.PsiFile; 5 | import org.antlr.jetbrains.st4plugin.Icons; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | import javax.swing.*; 9 | 10 | public class STGroupRootItemPresentation implements ItemPresentation { 11 | private final PsiFile file; 12 | 13 | public STGroupRootItemPresentation(PsiFile file) { 14 | this.file = file; 15 | } 16 | 17 | @Nullable 18 | @Override 19 | public String getPresentableText() { 20 | return file.getName(); 21 | } 22 | 23 | @Nullable 24 | @Override 25 | public String getLocationString() { 26 | return null; 27 | } 28 | 29 | @Nullable 30 | @Override 31 | public Icon getIcon(boolean unused) { 32 | return Icons.STG_FILE; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/resources/icons/bigstring-nonl.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/structview/STGroupItemPresentation.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.structview; 2 | 3 | import com.intellij.navigation.ItemPresentation; 4 | import org.antlr.jetbrains.st4plugin.Icons; 5 | import org.antlr.v4.runtime.tree.ParseTree; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | import javax.swing.*; 9 | 10 | public abstract class STGroupItemPresentation implements ItemPresentation { 11 | protected ParseTree node; 12 | 13 | public STGroupItemPresentation(ParseTree node) { 14 | this.node = node; 15 | } 16 | 17 | @Nullable 18 | @Override 19 | public Icon getIcon(boolean unused) { 20 | if (node.getParent() == null) return null; 21 | return Icons.STG_FILE; 22 | } 23 | 24 | @Nullable 25 | @Override 26 | public String getPresentableText() { 27 | return "n/a"; 28 | } 29 | 30 | @Nullable 31 | @Override 32 | public String getLocationString() { 33 | return null; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/structview/STGroupTemplateDefTreeElement.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.structview; 2 | 3 | import com.intellij.extapi.psi.ASTWrapperPsiElement; 4 | import com.intellij.navigation.ItemPresentation; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | public class STGroupTemplateDefTreeElement extends STGroupStructureViewTreeElement { 8 | public STGroupTemplateDefTreeElement(ASTWrapperPsiElement psiElement) { 9 | super(psiElement); 10 | } 11 | 12 | @NotNull 13 | @Override 14 | public ItemPresentation getPresentation() { 15 | return new STGroupTemplateDefItemPresentation((ASTWrapperPsiElement) psiElement); 16 | } 17 | 18 | @Override 19 | public boolean canNavigate() { 20 | return true; 21 | } 22 | 23 | @Override 24 | public boolean canNavigateToSource() { 25 | return true; 26 | } 27 | 28 | @Override 29 | public void navigate(boolean requestFocus) { 30 | psiElement.navigate(requestFocus); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/psi/TemplateContentElement.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.psi; 2 | 3 | import com.intellij.extapi.psi.ASTWrapperPsiElement; 4 | import com.intellij.lang.ASTNode; 5 | import com.intellij.psi.LiteralTextEscaper; 6 | import com.intellij.psi.PsiLanguageInjectionHost; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public class TemplateContentElement extends ASTWrapperPsiElement implements PsiLanguageInjectionHost { 10 | 11 | public TemplateContentElement(@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 | throw new UnsupportedOperationException(); 23 | } 24 | 25 | @NotNull 26 | @Override 27 | public LiteralTextEscaper createLiteralTextEscaper() { 28 | return new TemplateContentLiteralTextEscaper(this); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/psi/STFile.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.psi; 2 | 3 | import com.intellij.extapi.psi.PsiFileBase; 4 | import com.intellij.openapi.fileTypes.FileType; 5 | import com.intellij.psi.FileViewProvider; 6 | import org.antlr.jetbrains.st4plugin.Icons; 7 | import org.antlr.jetbrains.st4plugin.STFileType; 8 | import org.antlr.jetbrains.st4plugin.STLanguage; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | import javax.swing.*; 13 | 14 | public class STFile extends PsiFileBase { 15 | 16 | protected STFile(@NotNull FileViewProvider viewProvider) { 17 | super(viewProvider, STLanguage.INSTANCE); 18 | } 19 | 20 | @NotNull 21 | @Override 22 | public FileType getFileType() { 23 | return STFileType.INSTANCE; 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | return "String Template file"; 29 | } 30 | 31 | @Nullable 32 | @Override 33 | public Icon getIcon(int flags) { 34 | return Icons.STG_FILE; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/psi/STGroupFile.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.psi; 2 | 3 | import com.intellij.extapi.psi.PsiFileBase; 4 | import com.intellij.openapi.fileTypes.FileType; 5 | import com.intellij.psi.FileViewProvider; 6 | import org.antlr.jetbrains.st4plugin.Icons; 7 | import org.antlr.jetbrains.st4plugin.STGroupFileType; 8 | import org.antlr.jetbrains.st4plugin.STGroupLanguage; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | import javax.swing.*; 13 | 14 | public class STGroupFile extends PsiFileBase { 15 | 16 | protected STGroupFile(@NotNull FileViewProvider viewProvider) { 17 | super(viewProvider, STGroupLanguage.INSTANCE); 18 | } 19 | 20 | @NotNull 21 | @Override 22 | public FileType getFileType() { 23 | return STGroupFileType.INSTANCE; 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | return "String Template group file"; 29 | } 30 | 31 | @Nullable 32 | @Override 33 | public Icon getIcon(int flags) { 34 | return Icons.STG_FILE; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/STFileType.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin; 2 | 3 | import com.intellij.openapi.fileTypes.LanguageFileType; 4 | import com.intellij.openapi.fileTypes.TemplateLanguageFileType; 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | import javax.swing.*; 9 | 10 | public class STFileType extends LanguageFileType implements TemplateLanguageFileType { 11 | public static final STFileType INSTANCE = new STFileType(); 12 | 13 | protected STFileType() { 14 | super(STLanguage.INSTANCE); 15 | } 16 | 17 | @NotNull 18 | @Override 19 | public String getName() { 20 | return "StringTemplate v4 template file"; 21 | } 22 | 23 | @NotNull 24 | @Override 25 | public String getDescription() { 26 | return "StringTemplate v4 template file"; 27 | } 28 | 29 | @NotNull 30 | @Override 31 | public String getDefaultExtension() { 32 | return "st"; 33 | } 34 | 35 | @Nullable 36 | @Override 37 | public Icon getIcon() { 38 | return Icons.STG_FILE; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/STGroupFileType.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin; 2 | 3 | import com.intellij.openapi.fileTypes.LanguageFileType; 4 | import com.intellij.openapi.fileTypes.TemplateLanguageFileType; 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | import javax.swing.*; 9 | 10 | public class STGroupFileType extends LanguageFileType implements TemplateLanguageFileType { 11 | public static final STGroupFileType INSTANCE = new STGroupFileType(); 12 | 13 | protected STGroupFileType() { 14 | super(STGroupLanguage.INSTANCE); 15 | } 16 | 17 | @NotNull 18 | @Override 19 | public String getName() { 20 | return "StringTemplate v4 template group file"; 21 | } 22 | 23 | @NotNull 24 | @Override 25 | public String getDescription() { 26 | return "StringTemplate v4 template group file"; 27 | } 28 | 29 | @NotNull 30 | @Override 31 | public String getDefaultExtension() { 32 | return "stg"; 33 | } 34 | 35 | @Nullable 36 | @Override 37 | public Icon getIcon() { 38 | return Icons.STG_FILE; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/structview/STGroupStructureViewModel.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.structview; 2 | 3 | import com.intellij.ide.structureView.StructureViewModel; 4 | import com.intellij.ide.structureView.StructureViewModelBase; 5 | import com.intellij.ide.structureView.StructureViewTreeElement; 6 | import com.intellij.ide.util.treeView.smartTree.Sorter; 7 | import com.intellij.psi.PsiFile; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | public class STGroupStructureViewModel extends StructureViewModelBase implements 11 | StructureViewModel.ElementInfoProvider { 12 | 13 | public STGroupStructureViewModel(PsiFile psiFile) { 14 | super(psiFile, new STGroupRootTreeElement(psiFile)); 15 | } 16 | 17 | @NotNull 18 | @Override 19 | public Sorter[] getSorters() { 20 | return new Sorter[]{Sorter.ALPHA_SORTER}; 21 | } 22 | 23 | @Override 24 | public boolean isAlwaysShowsPlus(StructureViewTreeElement element) { 25 | return false; 26 | } 27 | 28 | @Override 29 | public boolean isAlwaysLeaf(StructureViewTreeElement element) { 30 | return element instanceof STGroupTemplateDefTreeElement; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/highlight/STGroupBraceMatcher.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.highlight; 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.antlr.jetbrains.st4plugin.parsing.STGLexer; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | import static org.antlr.jetbrains.st4plugin.psi.STGroupTokenTypes.getTokenElementType; 12 | 13 | public class STGroupBraceMatcher implements PairedBraceMatcher { 14 | 15 | private static final BracePair[] PAIRS = { 16 | new BracePair(getTokenElementType(STGLexer.LBRACK), getTokenElementType(STGLexer.RBRACK), true), 17 | new BracePair(getTokenElementType(STGLexer.LPAREN), getTokenElementType(STGLexer.RPAREN), true) 18 | }; 19 | 20 | @NotNull 21 | @Override 22 | public BracePair[] getPairs() { 23 | return PAIRS; 24 | } 25 | 26 | @Override 27 | public boolean isPairedBracesAllowedBeforeType(@NotNull IElementType lbraceType, @Nullable IElementType contextType) { 28 | return true; 29 | } 30 | 31 | @Override 32 | public int getCodeConstructStart(PsiFile file, int openingBraceOffset) { 33 | return openingBraceOffset; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Terence Parr 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Gradle 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle 3 | 4 | name: Java CI 5 | 6 | on: [ push, pull_request ] 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | env: 16 | # see https://www.jetbrains.com/idea/download/previous.html 17 | # and https://www.jetbrains.com/intellij-repository/snapshots/ 18 | - IDEA_VERSION: IC-2022.2.5 19 | - IDEA_VERSION: IC-2022.3.1 20 | - IDEA_VERSION: IC-2023.2.2 21 | - IDEA_VERSION: IU-LATEST-EAP-SNAPSHOT 22 | 23 | steps: 24 | - uses: actions/checkout@v2 25 | - name: Set up JDK 17 26 | uses: actions/setup-java@v1 27 | with: 28 | java-version: 17 29 | - name: Build with Gradle 30 | run: | 31 | ./gradlew -PideaVersion=${IDEA_VERSION} buildPlugin 32 | echo "DIST_FILE=$(find build/distributions/ -name 'jetbrains-plugin-st4-*.zip' -exec basename {} .zip \;)" >> $GITHUB_ENV 33 | env: ${{ matrix.env }} 34 | - name: Archive distribution artifact 35 | uses: actions/upload-artifact@v2 36 | with: 37 | name: ${{env.DIST_FILE}} 38 | path: build/distributions/jetbrains-plugin-st4-*.zip 39 | if: matrix.env.IDEA_VERSION == 'IC-2022.2.5' 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IntelliJ Plugin for StringTemplate v4 2 | [![Java CI](https://github.com/antlr/jetbrains-plugin-st4/workflows/Java%20CI/badge.svg?branch=master)](https://github.com/antlr/jetbrains-plugin-st4/actions) [![Latest version](https://img.shields.io/jetbrains/plugin/v/8041.svg?label=latest%20version)](https://plugins.jetbrains.com/plugin/8041-stringtemplate-v4-plugin) ![Downloads](https://img.shields.io/jetbrains/plugin/d/8041.svg) 3 | 4 | A plugin that adds support for StringTemplate v4 to [IntelliJ](https://www.jetbrains.com/idea/)-based IDEs (version 15.x and later). 5 | 6 | It understands `.stg` and `.st` files. For example, 7 | 8 | 9 | 10 | See the [plugin page](https://plugins.jetbrains.com/plugin/8041?pr=) for more information. 11 | 12 | # Highlighting the target language 13 | 14 | You can configure `Template Data Languages` to make the editor highlight the content around StringTemplate tags. 15 | For example, if your template is used to generate Java code, you can go to `File | Settings | Languages & Frameworks | Template Data Languages` 16 | and configure which language to highlight: 17 | 18 | 19 | 20 | The editor will now highlight Java parts around ST tags: 21 | 22 | 23 | 24 | # Building and contributing 25 | 26 | To build the plugin: 27 | `gradlew buildPlugin` 28 | 29 | To run the plugin: 30 | `gradlew runIde` 31 | 32 | When submitting a pull request, make sure your name is in the `contributors.txt` file. 33 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/highlight/STBraceMatcher.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.highlight; 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.antlr.jetbrains.st4plugin.parsing.STLexer; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | import static org.antlr.jetbrains.st4plugin.psi.STTokenTypes.getTokenElementType; 12 | 13 | public class STBraceMatcher implements PairedBraceMatcher { 14 | 15 | private static final BracePair[] PAIRS = { 16 | new BracePair(getTokenElementType(STLexer.LBRACK), getTokenElementType(STLexer.RBRACK), true), 17 | new BracePair(getTokenElementType(STLexer.LDELIM), getTokenElementType(STLexer.RDELIM), true), 18 | new BracePair(getTokenElementType(STLexer.LBRACE), getTokenElementType(STLexer.RBRACE), true), 19 | new BracePair(getTokenElementType(STLexer.LPAREN), getTokenElementType(STLexer.RPAREN), true) 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 true; 31 | } 32 | 33 | @Override 34 | public int getCodeConstructStart(PsiFile file, int openingBraceOffset) { 35 | return openingBraceOffset; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/highlight/STGroupSemanticHighlightAnnotator.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.highlight; 2 | 3 | import com.intellij.lang.annotation.AnnotationHolder; 4 | import com.intellij.lang.annotation.Annotator; 5 | import com.intellij.openapi.editor.DefaultLanguageHighlighterColors; 6 | import com.intellij.openapi.editor.colors.TextAttributesKey; 7 | import com.intellij.psi.PsiElement; 8 | import org.antlr.jetbrains.st4plugin.parsing.STGLexer; 9 | import org.antlr.jetbrains.st4plugin.parsing.STGParser; 10 | import org.antlr.jetbrains.st4plugin.psi.STGroupTokenTypes; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | import static com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey; 14 | import static org.antlr.jetbrains.st4plugin.psi.STGroupTokenTypes.getRuleElementType; 15 | 16 | /** 17 | * Highlights a group's formal args differently from its name. 18 | */ 19 | public class STGroupSemanticHighlightAnnotator implements Annotator { 20 | 21 | public static final TextAttributesKey TEMPLATE_PARAM = createTextAttributesKey("STGroup_TEMPLATE_PARAM", DefaultLanguageHighlighterColors.INSTANCE_FIELD); 22 | 23 | @Override 24 | public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) { 25 | if (element.getNode().getElementType() == STGroupTokenTypes.getTokenElementType(STGLexer.ID)) { 26 | if (element.getParent().getNode().getElementType() == getRuleElementType(STGParser.RULE_formalArg)) { 27 | holder.createInfoAnnotation(element, null) 28 | .setTextAttributes(TEMPLATE_PARAM); 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/psi/STTokenTypes.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.psi; 2 | 3 | import com.intellij.util.LazyInitializer; 4 | import org.antlr.intellij.adaptor.lexer.PSIElementTypeFactory; 5 | import org.antlr.intellij.adaptor.lexer.RuleIElementType; 6 | import org.antlr.intellij.adaptor.lexer.TokenIElementType; 7 | import org.antlr.jetbrains.st4plugin.STLanguage; 8 | import org.antlr.jetbrains.st4plugin.parsing.STLexer; 9 | import org.antlr.jetbrains.st4plugin.parsing.STParser; 10 | import org.intellij.lang.annotations.MagicConstant; 11 | 12 | import java.util.List; 13 | 14 | public final class STTokenTypes { 15 | 16 | private static final LazyInitializer.LazyValue> TOKEN_ELEMENT_TYPES = LazyInitializer.create(() -> { 17 | initIElementTypes(); 18 | return PSIElementTypeFactory.getTokenIElementTypes(STLanguage.INSTANCE); 19 | }); 20 | private static final LazyInitializer.LazyValue> RULE_ELEMENT_TYPES = LazyInitializer.create(() -> { 21 | initIElementTypes(); 22 | return PSIElementTypeFactory.getRuleIElementTypes(STLanguage.INSTANCE); 23 | }); 24 | 25 | public static void initIElementTypes() { 26 | PSIElementTypeFactory.defineLanguageIElementTypes( 27 | STLanguage.INSTANCE, 28 | STLexer.tokenNames, 29 | STParser.ruleNames 30 | ); 31 | } 32 | 33 | public static RuleIElementType getRuleElementType(@MagicConstant(valuesFromClass = STParser.class) int ruleIndex) { 34 | return RULE_ELEMENT_TYPES.get().get(ruleIndex); 35 | } 36 | 37 | public static TokenIElementType getTokenElementType(@MagicConstant(valuesFromClass = STLexer.class) int ruleIndex) { 38 | return TOKEN_ELEMENT_TYPES.get().get(ruleIndex); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/psi/STGroupTokenTypes.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.psi; 2 | 3 | import com.intellij.util.LazyInitializer; 4 | import org.antlr.intellij.adaptor.lexer.PSIElementTypeFactory; 5 | import org.antlr.intellij.adaptor.lexer.RuleIElementType; 6 | import org.antlr.intellij.adaptor.lexer.TokenIElementType; 7 | import org.antlr.jetbrains.st4plugin.STGroupLanguage; 8 | import org.antlr.jetbrains.st4plugin.parsing.STGLexer; 9 | import org.antlr.jetbrains.st4plugin.parsing.STGParser; 10 | import org.intellij.lang.annotations.MagicConstant; 11 | 12 | import java.util.List; 13 | 14 | public final class STGroupTokenTypes { 15 | 16 | private static final LazyInitializer.LazyValue> TOKEN_ELEMENT_TYPES = LazyInitializer.create(() -> { 17 | initIElementTypes(); 18 | return PSIElementTypeFactory.getTokenIElementTypes(STGroupLanguage.INSTANCE); 19 | }); 20 | private static final LazyInitializer.LazyValue> RULE_ELEMENT_TYPES = LazyInitializer.create(() -> { 21 | initIElementTypes(); 22 | return PSIElementTypeFactory.getRuleIElementTypes(STGroupLanguage.INSTANCE); 23 | }); 24 | 25 | public static void initIElementTypes() { 26 | PSIElementTypeFactory.defineLanguageIElementTypes( 27 | STGroupLanguage.INSTANCE, 28 | STGLexer.tokenNames, 29 | STGParser.ruleNames 30 | ); 31 | } 32 | 33 | public static RuleIElementType getRuleElementType(@MagicConstant(valuesFromClass = STGParser.class) int ruleIndex) { 34 | return RULE_ELEMENT_TYPES.get().get(ruleIndex); 35 | } 36 | 37 | public static TokenIElementType getTokenElementType(@MagicConstant(valuesFromClass = STGLexer.class) int ruleIndex) { 38 | return TOKEN_ELEMENT_TYPES.get().get(ruleIndex); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/structview/STGroupRootTreeElement.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.structview; 2 | 3 | import com.intellij.extapi.psi.ASTWrapperPsiElement; 4 | import com.intellij.ide.util.treeView.smartTree.TreeElement; 5 | import com.intellij.navigation.ItemPresentation; 6 | import com.intellij.psi.PsiElement; 7 | import com.intellij.psi.PsiFile; 8 | import org.antlr.jetbrains.st4plugin.parsing.STGParser; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | import java.util.Arrays; 12 | 13 | import static org.antlr.jetbrains.st4plugin.psi.STGroupTokenTypes.getRuleElementType; 14 | 15 | public class STGroupRootTreeElement extends STGroupStructureViewTreeElement { 16 | 17 | public STGroupRootTreeElement(PsiFile psiFile) { 18 | super(psiFile); 19 | } 20 | 21 | @NotNull 22 | @Override 23 | public ItemPresentation getPresentation() { 24 | return new STGroupRootItemPresentation((PsiFile) psiElement); 25 | } 26 | 27 | @NotNull 28 | @Override 29 | public TreeElement[] getChildren() { 30 | return Arrays.stream(this.psiElement.getChildren()) 31 | .filter(e -> e.getNode().getElementType() == getRuleElementType(STGParser.RULE_group)) 32 | .findFirst() 33 | .map(group -> Arrays.stream(group.getChildren()) 34 | .filter(this::shouldShowInStructureView) 35 | .map(e -> new STGroupTemplateDefTreeElement((ASTWrapperPsiElement) e)) 36 | .toArray(TreeElement[]::new)) 37 | .orElse(TreeElement.EMPTY_ARRAY); 38 | } 39 | 40 | private boolean shouldShowInStructureView(@NotNull PsiElement child) { 41 | return child.getNode().getElementType() == getRuleElementType(STGParser.RULE_template) 42 | || child.getNode().getElementType() == getRuleElementType(STGParser.RULE_dict); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/structview/STGroupStructureViewTreeElement.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.structview; 2 | 3 | import com.intellij.ide.structureView.StructureViewTreeElement; 4 | import com.intellij.ide.util.treeView.smartTree.SortableTreeElement; 5 | import com.intellij.ide.util.treeView.smartTree.TreeElement; 6 | import com.intellij.navigation.ItemPresentation; 7 | import com.intellij.psi.NavigatablePsiElement; 8 | import org.antlr.jetbrains.st4plugin.Icons; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | import javax.swing.*; 13 | 14 | public abstract class STGroupStructureViewTreeElement 15 | implements StructureViewTreeElement, ItemPresentation, SortableTreeElement { 16 | 17 | protected NavigatablePsiElement psiElement; 18 | 19 | public STGroupStructureViewTreeElement(NavigatablePsiElement psiElement) { 20 | this.psiElement = psiElement; 21 | } 22 | 23 | @Nullable 24 | @Override 25 | public Icon getIcon(boolean unused) { 26 | return Icons.STG_FILE; 27 | } 28 | 29 | @Nullable 30 | @Override 31 | public String getPresentableText() { 32 | return "TODO"; 33 | } 34 | 35 | @Override 36 | public void navigate(boolean requestFocus) { 37 | psiElement.navigate(requestFocus); 38 | } 39 | 40 | @Override 41 | public boolean canNavigate() { 42 | return true; 43 | } 44 | 45 | @Override 46 | public boolean canNavigateToSource() { 47 | return false; 48 | } 49 | 50 | @Nullable 51 | @Override 52 | public String getLocationString() { 53 | return null; 54 | } 55 | 56 | @Override 57 | public NavigatablePsiElement getValue() { 58 | return psiElement; 59 | } 60 | 61 | @NotNull 62 | @Override 63 | public String getAlphaSortKey() { 64 | return getPresentation().getPresentableText(); 65 | } 66 | 67 | @NotNull 68 | @Override 69 | public TreeElement[] getChildren() { 70 | return EMPTY_ARRAY; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/highlight/STEditorHighlighterProvider.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.highlight; 2 | 3 | import com.intellij.lang.Language; 4 | import com.intellij.openapi.editor.colors.EditorColorsScheme; 5 | import com.intellij.openapi.editor.ex.util.LayerDescriptor; 6 | import com.intellij.openapi.editor.ex.util.LayeredLexerEditorHighlighter; 7 | import com.intellij.openapi.editor.highlighter.EditorHighlighter; 8 | import com.intellij.openapi.fileTypes.EditorHighlighterProvider; 9 | import com.intellij.openapi.fileTypes.FileType; 10 | import com.intellij.openapi.fileTypes.SyntaxHighlighter; 11 | import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory; 12 | import com.intellij.openapi.project.Project; 13 | import com.intellij.openapi.vfs.VirtualFile; 14 | import com.intellij.psi.templateLanguages.TemplateDataLanguageMappings; 15 | import org.antlr.jetbrains.st4plugin.STGroupFileType; 16 | import org.antlr.jetbrains.st4plugin.parsing.STLexer; 17 | import org.jetbrains.annotations.NotNull; 18 | import org.jetbrains.annotations.Nullable; 19 | 20 | import static org.antlr.jetbrains.st4plugin.psi.STTokenTypes.getTokenElementType; 21 | 22 | public class STEditorHighlighterProvider implements EditorHighlighterProvider { 23 | 24 | @Override 25 | public EditorHighlighter getEditorHighlighter(@Nullable Project project, @NotNull FileType fileType, @Nullable VirtualFile virtualFile, @NotNull EditorColorsScheme colors) { 26 | return new STEditorHighlighter(virtualFile, project, fileType, colors); 27 | } 28 | } 29 | 30 | /** 31 | * Highlights the "outer language", i.e. the target language. 32 | * The target language can be configured in the Template Data Languages settings. 33 | */ 34 | class STEditorHighlighter extends LayeredLexerEditorHighlighter { 35 | 36 | public STEditorHighlighter(@Nullable VirtualFile virtualFile, 37 | @Nullable Project project, 38 | @NotNull FileType fileType, 39 | @NotNull EditorColorsScheme scheme) { 40 | super(fileType == STGroupFileType.INSTANCE ? new STGroupSyntaxHighlighter() : new STSyntaxHighlighter(), scheme); 41 | 42 | if (project != null && virtualFile != null) { 43 | Language language = TemplateDataLanguageMappings.getInstance(project).getMapping(virtualFile); 44 | 45 | if (language != null) { 46 | SyntaxHighlighter outerHighlighter = SyntaxHighlighterFactory.getSyntaxHighlighter(language.getAssociatedFileType(), project, virtualFile); 47 | 48 | if (outerHighlighter != null) { 49 | registerLayer(getTokenElementType(STLexer.TEXT), new LayerDescriptor(outerHighlighter, "")); 50 | } 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/highlight/STSyntaxHighlighter.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.highlight; 2 | 3 | import com.intellij.lexer.Lexer; 4 | import com.intellij.openapi.editor.DefaultLanguageHighlighterColors; 5 | import com.intellij.openapi.editor.HighlighterColors; 6 | import com.intellij.openapi.editor.colors.TextAttributesKey; 7 | import com.intellij.openapi.fileTypes.SyntaxHighlighterBase; 8 | import com.intellij.psi.tree.IElementType; 9 | import org.antlr.intellij.adaptor.lexer.ANTLRLexerAdaptor; 10 | import org.antlr.jetbrains.st4plugin.STLanguage; 11 | import org.antlr.jetbrains.st4plugin.parsing.STLexer; 12 | import org.antlr.jetbrains.st4plugin.psi.STTokenTypes; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | import java.util.List; 16 | import java.util.stream.Collectors; 17 | import java.util.stream.Stream; 18 | 19 | import static com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey; 20 | import static org.antlr.jetbrains.st4plugin.psi.STTokenTypes.getTokenElementType; 21 | 22 | public class STSyntaxHighlighter extends SyntaxHighlighterBase { 23 | 24 | public static final TextAttributesKey STGroup_TEMPLATE_TEXT = 25 | createTextAttributesKey("STGroup_TEMPLATE_TEXT", DefaultLanguageHighlighterColors.TEMPLATE_LANGUAGE_COLOR); 26 | 27 | private static final List KEYWORDS = Stream.of( 28 | STLexer.IF, STLexer.ELSE, STLexer.END, STLexer.TRUE, 29 | STLexer.FALSE, STLexer.ELSEIF, STLexer.ENDIF, STLexer.SUPER 30 | ).map(STTokenTypes::getTokenElementType).collect(Collectors.toList()); 31 | 32 | @NotNull 33 | @Override 34 | public Lexer getHighlightingLexer() { 35 | return new ANTLRLexerAdaptor(STLanguage.INSTANCE, new STLexer(null)); 36 | } 37 | 38 | @NotNull 39 | @Override 40 | public TextAttributesKey[] getTokenHighlights(IElementType tokenType) { 41 | TextAttributesKey key; 42 | 43 | if (KEYWORDS.contains(tokenType)) { 44 | key = STGroupSyntaxHighlighter.KEYWORD; 45 | } else if (getTokenElementType(STLexer.STRING).equals(tokenType) 46 | || getTokenElementType(STLexer.TEXT).equals(tokenType)) { 47 | key = STGroup_TEMPLATE_TEXT; 48 | } else if (getTokenElementType(STLexer.TMPL_COMMENT).equals(tokenType)) { 49 | key = STGroupSyntaxHighlighter.BLOCK_COMMENT; 50 | } else if (getTokenElementType(STLexer.SUB_ERR_CHAR).equals(tokenType)) { 51 | key = HighlighterColors.BAD_CHARACTER; 52 | } else if (getTokenElementType(STLexer.ESCAPE).equals(tokenType)) { 53 | key = DefaultLanguageHighlighterColors.VALID_STRING_ESCAPE; 54 | } else { 55 | return STGroupSyntaxHighlighter.NO_ATTRIBUTES; 56 | } 57 | 58 | return new TextAttributesKey[]{key}; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/psi/TemplateContentLiteralTextEscaper.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.psi; 2 | 3 | import com.intellij.openapi.util.TextRange; 4 | import com.intellij.psi.LiteralTextEscaper; 5 | import com.intellij.psi.PsiLanguageInjectionHost; 6 | import org.antlr.jetbrains.st4plugin.parsing.STGLexer; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import static org.antlr.jetbrains.st4plugin.psi.STGroupTokenTypes.getTokenElementType; 10 | 11 | /** 12 | * Removes escaped quotes from subtemplates when parsing them as {@link org.antlr.jetbrains.st4plugin.STLanguage} 13 | * files. 14 | */ 15 | class TemplateContentLiteralTextEscaper extends LiteralTextEscaper { 16 | 17 | private int[] offsetsFromDecodedToHost = new int[0]; 18 | 19 | public TemplateContentLiteralTextEscaper(TemplateContentElement templateContentElement) { 20 | super(templateContentElement); 21 | } 22 | 23 | @Override 24 | public boolean decode(@NotNull TextRange rangeInsideHost, @NotNull StringBuilder outChars) { 25 | String subTemplate = rangeInsideHost.substring(myHost.getText()); 26 | 27 | if (myHost.getNode().getFirstChildNode().getElementType() == getTokenElementType(STGLexer.STRING) 28 | && subTemplate.indexOf('\\') >= 0) { 29 | 30 | offsetsFromDecodedToHost = new int[subTemplate.length() + 1]; 31 | 32 | for (int indexInHost = 0, indexInDecoded = 0; indexInHost < subTemplate.length(); indexInHost++) { 33 | if (subTemplate.charAt(indexInHost) == '\\' 34 | && indexInHost + 1 < subTemplate.length() 35 | && subTemplate.charAt(indexInHost + 1) == '"') { 36 | 37 | indexInHost++; // skip '\' 38 | } 39 | 40 | outChars.append(subTemplate.charAt(indexInHost)); 41 | 42 | offsetsFromDecodedToHost[indexInDecoded++] = indexInHost; 43 | offsetsFromDecodedToHost[indexInDecoded] = indexInHost + 1; 44 | } 45 | } else { 46 | offsetsFromDecodedToHost = new int[subTemplate.length() + 1]; 47 | for (int i = 0; i < offsetsFromDecodedToHost.length; i++) { 48 | offsetsFromDecodedToHost[i] = i; 49 | } 50 | outChars.append(subTemplate); 51 | } 52 | 53 | return true; 54 | } 55 | 56 | @Override 57 | public int getOffsetInHost(int offsetInDecoded, @NotNull TextRange rangeInsideHost) { 58 | int result = offsetInDecoded < offsetsFromDecodedToHost.length ? offsetsFromDecodedToHost[offsetInDecoded] : -1; 59 | if (result == -1) { 60 | return -1; 61 | } 62 | 63 | return Math.min(result, rangeInsideHost.getLength()) + rangeInsideHost.getStartOffset(); 64 | } 65 | 66 | @Override 67 | public boolean isOneLine() { 68 | return false; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /contributors.txt: -------------------------------------------------------------------------------- 1 | ANTLR Project Contributors Certification of Origin and Rights 2 | 3 | *This is the standard contributors.txt file for the ANTLR project 4 | but we explicitly include the StringTemplate jetbrains plugin here.* 5 | 6 | All contributors to ANTLR v4 must formally agree to abide by this 7 | certificate of origin by signing on the bottom with their github 8 | userid, full name, email address (you can obscure your e-mail, but it 9 | must be computable by human), and date. 10 | 11 | By signing this agreement, you are warranting and representing that 12 | you have the right to release code contributions or other content free 13 | of any obligations to third parties and are granting Terence Parr and 14 | ANTLR project contributors, henceforth referred to as The ANTLR 15 | Project, a license to incorporate it into The ANTLR Project tools 16 | (such as ANTLRWorks and StringTemplate) or related works under the BSD 17 | license. You understand that The ANTLR Project may or may not 18 | incorporate your contribution and you warrant and represent the 19 | following: 20 | 21 | 1. I am the creator of all my contributions. I am the author of all 22 | contributed work submitted and further warrant and represent that 23 | such work is my original creation and I have the right to license 24 | it to The ANTLR Project for release under the 3-clause BSD 25 | license. I hereby grant The ANTLR Project a nonexclusive, 26 | irrevocable, royalty-free, worldwide license to reproduce, 27 | distribute, prepare derivative works, and otherwise use this 28 | contribution as part of the ANTLR project, associated 29 | documentation, books, and tools at no cost to The ANTLR Project. 30 | 31 | 2. I have the right to submit. This submission does not violate the 32 | rights of any person or entity and that I have legal authority over 33 | this submission and to make this certification. 34 | 35 | 3. If I violate another's rights, liability lies with me. I agree to 36 | defend, indemnify, and hold The ANTLR Project and ANTLR users 37 | harmless from any claim or demand, including reasonable attorney 38 | fees, made by any third party due to or arising out of my violation 39 | of these terms and conditions or my violation of the rights of 40 | another person or entity. 41 | 42 | 4. I understand and agree that this project and the contribution are 43 | public and that a record of the contribution (including all 44 | personal information I submit with it, including my sign-off) is 45 | maintained indefinitely and may be redistributed consistent with 46 | this project or the open source license indicated in the file. 47 | 48 | I have read this agreement and do so certify by adding my signoff to 49 | the end of the following contributors list. 50 | 51 | CONTRIBUTORS: 52 | 53 | YYYY/MM/DD, github id, Full name, email 54 | 2015/11/09, parrt, Terence Parr, parrt@antlr.org 55 | 2019/01/16, syakovlevdalet, Sergey Yakovlev, syakovlev@dalet.com 56 | 2019/01/17, bjansen, Bastien Jansen, bastien.jansen@gmx.com 57 | 2020/05/19, FHannes, Frédéric Hannes, frederic.hannes@gmail.com 58 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/structview/STGroupTemplateDefItemPresentation.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.structview; 2 | 3 | import com.intellij.extapi.psi.ASTWrapperPsiElement; 4 | import com.intellij.lang.ASTNode; 5 | import com.intellij.navigation.ItemPresentation; 6 | import com.intellij.psi.tree.IElementType; 7 | import com.intellij.psi.tree.TokenSet; 8 | import org.antlr.jetbrains.st4plugin.Icons; 9 | import org.antlr.jetbrains.st4plugin.parsing.STGLexer; 10 | import org.antlr.jetbrains.st4plugin.parsing.STGParser; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | import javax.swing.*; 14 | import java.util.Arrays; 15 | import java.util.stream.Collectors; 16 | 17 | import static org.antlr.jetbrains.st4plugin.psi.STGroupTokenTypes.getRuleElementType; 18 | import static org.antlr.jetbrains.st4plugin.psi.STGroupTokenTypes.getTokenElementType; 19 | 20 | public class STGroupTemplateDefItemPresentation implements ItemPresentation { 21 | 22 | private final ASTWrapperPsiElement psiElement; 23 | 24 | public STGroupTemplateDefItemPresentation(ASTWrapperPsiElement psiElement) { 25 | this.psiElement = psiElement; 26 | } 27 | 28 | @Nullable 29 | @Override 30 | public String getPresentableText() { 31 | ASTNode id = psiElement.getNode().findChildByType(getTokenElementType(STGLexer.ID)); 32 | 33 | if (id == null) { 34 | return null; 35 | } 36 | 37 | StringBuilder text = new StringBuilder(id.getText()); 38 | 39 | ASTNode args = psiElement.getNode().findChildByType(getRuleElementType(STGParser.RULE_formalArgs)); 40 | 41 | if (args != null) { 42 | text.append('('); 43 | 44 | ASTNode[] argList = args.getChildren(TokenSet.create(getRuleElementType(STGParser.RULE_formalArg))); 45 | 46 | text.append(Arrays.stream(argList).map(ASTNode::getText).collect(Collectors.joining(", "))); 47 | 48 | text.append(')'); 49 | } 50 | 51 | return text.toString(); 52 | } 53 | 54 | @Nullable 55 | @Override 56 | public String getLocationString() { 57 | return null; 58 | } 59 | 60 | @Nullable 61 | @Override 62 | public Icon getIcon(boolean unused) { 63 | if (psiElement.getNode().getElementType() == getRuleElementType(STGParser.RULE_dict)) { 64 | return Icons.DICT; 65 | } 66 | 67 | ASTNode content = psiElement.getNode().findChildByType(getRuleElementType(STGParser.RULE_templateContent)); 68 | IElementType elementType = null; 69 | 70 | if (content != null) { 71 | ASTNode firstChild = content.getFirstChildNode(); 72 | 73 | if (firstChild != null) { 74 | elementType = firstChild.getElementType(); 75 | } 76 | } 77 | 78 | if (elementType == getTokenElementType(STGLexer.STRING)) { 79 | return Icons.STRING; 80 | } else if (elementType == getTokenElementType(STGLexer.BIGSTRING_NO_NL)) { 81 | return Icons.BIGSTRING_NONL; 82 | } 83 | 84 | return Icons.BIGSTRING; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /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 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /src/main/resources/st-java-injections.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ST (org.stringtemplate.v4) 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | STGroup (org.stringtemplate.v4) 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | STGroupString (org.stringtemplate.v4) 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/folding/STFoldingBuilder.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.folding; 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.util.TextRange; 8 | import com.intellij.psi.PsiElement; 9 | import com.intellij.psi.util.PsiTreeUtil; 10 | import org.antlr.jetbrains.st4plugin.parsing.STLexer; 11 | import org.antlr.jetbrains.st4plugin.parsing.STParser; 12 | import org.antlr.jetbrains.st4plugin.psi.STFile; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | import java.util.List; 16 | 17 | import static org.antlr.jetbrains.st4plugin.psi.STTokenTypes.getRuleElementType; 18 | import static org.antlr.jetbrains.st4plugin.psi.STTokenTypes.getTokenElementType; 19 | 20 | public class STFoldingBuilder extends CustomFoldingBuilder { 21 | @Override 22 | protected void buildLanguageFoldRegions(@NotNull List descriptors, 23 | @NotNull PsiElement root, 24 | @NotNull Document document, 25 | boolean quick) { 26 | if (!(root instanceof STFile)) { 27 | return; 28 | } 29 | 30 | foldRegions(descriptors, root); 31 | foldIf(descriptors, root); 32 | } 33 | 34 | private void foldRegions(List descriptors, PsiElement root) { 35 | PsiTreeUtil.processElements(root, element -> { 36 | if (element.getNode().getElementType() == getRuleElementType(STParser.RULE_region)) { 37 | descriptors.add(new FoldingDescriptor(element, element.getTextRange())); 38 | } 39 | 40 | return true; 41 | }); 42 | } 43 | 44 | private void foldIf(List descriptors, PsiElement root) { 45 | PsiTreeUtil.processElements(root, element -> { 46 | if (element.getNode().getElementType() == getRuleElementType(STParser.RULE_ifstat)) { 47 | descriptors.add(new FoldingDescriptor(element, element.getTextRange())); 48 | } 49 | 50 | return true; 51 | }); 52 | } 53 | 54 | @Override 55 | protected String getLanguagePlaceholderText(@NotNull ASTNode node, @NotNull TextRange range) { 56 | if (node.getElementType() == getRuleElementType(STParser.RULE_region)) { 57 | ASTNode id = node.findChildByType(getTokenElementType(STLexer.ID)); 58 | 59 | return "<@" + (id == null ? "??" : id.getText()) + ">"; 60 | } else if (node.getElementType() == getRuleElementType(STParser.RULE_ifstat)) { 61 | ASTNode lDelim = node.findChildByType(getTokenElementType(STLexer.LDELIM)); 62 | ASTNode rDelim = node.findChildByType(getTokenElementType(STLexer.RDELIM)); 63 | 64 | if (lDelim != null && rDelim != null) { 65 | int startOffset = node.getStartOffset(); 66 | return node.getText().substring(lDelim.getStartOffset() - startOffset, rDelim.getStartOffset() + rDelim.getTextLength() - startOffset); 67 | } else { 68 | return ""; 69 | } 70 | } 71 | 72 | return "..."; 73 | } 74 | 75 | @Override 76 | protected boolean isRegionCollapsedByDefault(@NotNull ASTNode node) { 77 | return false; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/antlr/STGParser.g4: -------------------------------------------------------------------------------- 1 | /* [The "BSD license"] 2 | * Copyright (c) 2011-2014 Terence Parr 3 | * Copyright (c) 2013-2015 Gerald Rosenberg 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. The name of the author may not be used to endorse or promote products 15 | * derived from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | /* Antlr grammar for StringTemplate v4. 30 | * 31 | * Modified 2013.11.21 gbr 32 | * -- updated 33 | * Modified 2015.06.21 gbr 34 | * -- updated to use imported standard fragments 35 | */ 36 | 37 | parser grammar STGParser; 38 | 39 | options { 40 | language=Java; 41 | tokenVocab=STGLexer; 42 | } 43 | 44 | group 45 | : oldStyleHeader? delimiters? imports? 46 | ( template | dict )+ 47 | EOF 48 | ; 49 | 50 | // Accepted for retrocompatibility with V3 but ignored by the reference parser 51 | oldStyleHeader 52 | : GROUP ID (COLON ID)? (IMPLEMENTS ID (COMMA ID)* )? SEMI; 53 | 54 | delimiters 55 | : DELIMITERS STRING COMMA STRING 56 | ; 57 | 58 | imports 59 | : ( IMPORT STRING )+ 60 | ; 61 | 62 | template 63 | : ( AT ID DOT ID LPAREN RPAREN 64 | | ID LPAREN formalArgs? RPAREN 65 | ) 66 | TMPL_ASSIGN templateContent 67 | | ID TMPL_ASSIGN ID // alias one template to another 68 | ; 69 | 70 | templateContent 71 | : STRING // "..." 72 | | BIGSTRING // <<...>> 73 | | BIGSTRING_NO_NL // <%...%> 74 | ; 75 | 76 | formalArgs 77 | : formalArg ( COMMA formalArg )* 78 | ; 79 | 80 | formalArg 81 | : ID ( ASSIGN STRING 82 | | ASSIGN ANON_TEMPLATE 83 | | ASSIGN TRUE 84 | | ASSIGN FALSE 85 | | ASSIGN LBRACK RBRACK 86 | )? 87 | ; 88 | 89 | dict 90 | : ID TMPL_ASSIGN LBRACK dictPairs RBRACK 91 | ; 92 | 93 | dictPairs 94 | : keyValuePair ( COMMA keyValuePair )* ( COMMA defaultValuePair )? 95 | | defaultValuePair 96 | ; 97 | 98 | keyValuePair : STRING COLON keyValue ; 99 | defaultValuePair : DEFAULT COLON keyValue ; 100 | 101 | keyValue 102 | : BIGSTRING 103 | | BIGSTRING_NO_NL 104 | | ANON_TEMPLATE 105 | | STRING 106 | | TRUE 107 | | FALSE 108 | | LBRACK RBRACK 109 | | KEY 110 | ; 111 | 112 | junk : JUNK ; // define to over come https://github.com/antlr/antlr4/issues/1042 113 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/psi/STLanguageInjector.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.intellij.openapi.util.TextRange; 5 | import com.intellij.psi.InjectedLanguagePlaces; 6 | import com.intellij.psi.LanguageInjector; 7 | import com.intellij.psi.PsiElement; 8 | import com.intellij.psi.PsiLanguageInjectionHost; 9 | import com.intellij.psi.tree.TokenSet; 10 | import org.antlr.jetbrains.st4plugin.STLanguage; 11 | import org.antlr.jetbrains.st4plugin.parsing.STGLexer; 12 | import org.antlr.jetbrains.st4plugin.parsing.STGParser; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | import static org.antlr.jetbrains.st4plugin.parsing.LexerAdaptor.DELIMITERS_PREFIX; 16 | import static org.antlr.jetbrains.st4plugin.psi.STGroupTokenTypes.getRuleElementType; 17 | import static org.antlr.jetbrains.st4plugin.psi.STGroupTokenTypes.getTokenElementType; 18 | 19 | /** 20 | * Inject the {@link STLanguage} in {@link org.antlr.jetbrains.st4plugin.STGroupLanguage} subtemplates. 21 | */ 22 | public class STLanguageInjector implements LanguageInjector { 23 | 24 | @Override 25 | public void getLanguagesToInject(@NotNull PsiLanguageInjectionHost host, 26 | @NotNull InjectedLanguagePlaces injectionPlacesRegistrar) { 27 | 28 | if (host instanceof TemplateContentElement) { 29 | PsiElement firstChild = host.getFirstChild(); 30 | 31 | String delimiters = detectDelimiters(host); 32 | 33 | if (firstChild != null 34 | && (firstChild.getNode().getElementType() == getTokenElementType(STGLexer.BIGSTRING) || firstChild.getNode().getElementType() == getTokenElementType(STGLexer.BIGSTRING_NO_NL)) 35 | && host.getTextLength() > 4) { 36 | TextRange textRange = TextRange.create(2, host.getTextLength() - 2); 37 | injectionPlacesRegistrar.addPlace(STLanguage.INSTANCE, textRange, delimiters, null); 38 | } else if (firstChild != null 39 | && firstChild.getNode().getElementType() == getTokenElementType(STGLexer.STRING) 40 | && host.getTextLength() > 2) { 41 | TextRange textRange = TextRange.create(1, host.getTextLength() - 1); 42 | injectionPlacesRegistrar.addPlace(STLanguage.INSTANCE, textRange, delimiters, null); 43 | } 44 | } 45 | } 46 | 47 | /** 48 | * If the STGroup file contains a {@code delimiters "x", "y"} section, we pass those delimiters as a special 49 | * prefix to the lexer. The lexer will then detect this prefix and reconfigure itself to support the new 50 | * delimiters. 51 | */ 52 | private String detectDelimiters(@NotNull PsiLanguageInjectionHost host) { 53 | ASTNode root = host.getContainingFile().getFirstChild().getNode(); 54 | 55 | if (root.getElementType() == getRuleElementType(STGParser.RULE_group)) { 56 | ASTNode delimitersStatement = root.findChildByType(getRuleElementType(STGParser.RULE_delimiters)); 57 | 58 | if (delimitersStatement != null) { 59 | ASTNode[] strings = delimitersStatement.getChildren(TokenSet.create(getTokenElementType(STGLexer.STRING))); 60 | 61 | if (strings.length == 2 && strings[0].getTextLength() == 3 && strings[1].getTextLength() == 3) { 62 | return "" + DELIMITERS_PREFIX + strings[0].getText().charAt(1) + strings[1].getText().charAt(1); 63 | } 64 | } 65 | } 66 | return null; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/highlight/STGroupSyntaxHighlighter.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.highlight; 2 | 3 | import com.intellij.lexer.Lexer; 4 | import com.intellij.openapi.editor.DefaultLanguageHighlighterColors; 5 | import com.intellij.openapi.editor.HighlighterColors; 6 | import com.intellij.openapi.editor.colors.TextAttributesKey; 7 | import com.intellij.openapi.fileTypes.SyntaxHighlighterBase; 8 | import com.intellij.psi.tree.IElementType; 9 | import org.antlr.intellij.adaptor.lexer.ANTLRLexerAdaptor; 10 | import org.antlr.intellij.adaptor.lexer.TokenIElementType; 11 | import org.antlr.jetbrains.st4plugin.STGroupLanguage; 12 | import org.antlr.jetbrains.st4plugin.parsing.STGLexer; 13 | import org.antlr.jetbrains.st4plugin.psi.STGroupTokenTypes; 14 | import org.antlr.v4.runtime.Token; 15 | import org.jetbrains.annotations.NotNull; 16 | 17 | import java.util.List; 18 | import java.util.stream.Collectors; 19 | import java.util.stream.Stream; 20 | 21 | import static com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey; 22 | import static org.antlr.jetbrains.st4plugin.psi.STGroupTokenTypes.getTokenElementType; 23 | 24 | public class STGroupSyntaxHighlighter extends SyntaxHighlighterBase { 25 | 26 | public static final TextAttributesKey TEMPLATE_NAME = 27 | createTextAttributesKey("STGroup_TEMPLATE_NAME", DefaultLanguageHighlighterColors.INSTANCE_METHOD); 28 | public static final TextAttributesKey LINE_COMMENT = 29 | createTextAttributesKey("STGroup_LINE_COMMENT", DefaultLanguageHighlighterColors.LINE_COMMENT); 30 | public static final TextAttributesKey BLOCK_COMMENT = 31 | createTextAttributesKey("STGroup_BLOCK_COMMENT", DefaultLanguageHighlighterColors.BLOCK_COMMENT); 32 | public static final TextAttributesKey KEYWORD = 33 | createTextAttributesKey("STGroup_KEYWORD", DefaultLanguageHighlighterColors.KEYWORD); 34 | public static final TextAttributesKey STRING = 35 | createTextAttributesKey("STGroup_STRING", DefaultLanguageHighlighterColors.STRING); 36 | 37 | private static final List KEYWORDS = Stream.of( 38 | STGLexer.DELIMITERS, STGLexer.IMPORT, STGLexer.DEFAULT, STGLexer.KEY, STGLexer.GROUP 39 | ).map(STGroupTokenTypes::getTokenElementType).collect(Collectors.toList()); 40 | 41 | public static final TextAttributesKey[] NO_ATTRIBUTES = new TextAttributesKey[0]; 42 | 43 | @NotNull 44 | @Override 45 | public Lexer getHighlightingLexer() { 46 | return new ANTLRLexerAdaptor(STGroupLanguage.INSTANCE, new STGLexer(null)); 47 | } 48 | 49 | @NotNull 50 | @Override 51 | public TextAttributesKey[] getTokenHighlights(IElementType tokenType) { 52 | if (getTokenElementType(STGLexer.COMMENT).equals(tokenType)) { 53 | return new TextAttributesKey[]{BLOCK_COMMENT}; 54 | } else if (getTokenElementType(STGLexer.LINE_COMMENT).equals(tokenType)) { 55 | return new TextAttributesKey[]{LINE_COMMENT}; 56 | } else if (getTokenElementType(STGLexer.STRING).equals(tokenType)) { 57 | return new TextAttributesKey[]{STRING}; 58 | } else if (getTokenElementType(STGLexer.ID).equals(tokenType)) { 59 | return new TextAttributesKey[]{TEMPLATE_NAME}; 60 | } else if (KEYWORDS.contains(tokenType)) { 61 | return new TextAttributesKey[]{KEYWORD}; 62 | } else if (tokenType instanceof TokenIElementType && ((TokenIElementType) tokenType).getANTLRTokenType() == Token.INVALID_TYPE) { 63 | return new TextAttributesKey[]{HighlighterColors.BAD_CHARACTER}; 64 | } 65 | return NO_ATTRIBUTES; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/psi/STGroupParserDefinition.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.psi; 2 | 3 | import com.intellij.extapi.psi.ASTWrapperPsiElement; 4 | import com.intellij.lang.ASTNode; 5 | import com.intellij.lang.ParserDefinition; 6 | import com.intellij.lang.PsiParser; 7 | import com.intellij.lexer.Lexer; 8 | import com.intellij.openapi.project.Project; 9 | import com.intellij.psi.FileViewProvider; 10 | import com.intellij.psi.PsiElement; 11 | import com.intellij.psi.PsiFile; 12 | import com.intellij.psi.tree.IElementType; 13 | import com.intellij.psi.tree.IFileElementType; 14 | import com.intellij.psi.tree.TokenSet; 15 | import org.antlr.intellij.adaptor.lexer.ANTLRLexerAdaptor; 16 | import org.antlr.intellij.adaptor.lexer.PSIElementTypeFactory; 17 | import org.antlr.intellij.adaptor.parser.ANTLRParserAdaptor; 18 | import org.antlr.jetbrains.st4plugin.STGroupLanguage; 19 | import org.antlr.jetbrains.st4plugin.parsing.STGLexer; 20 | import org.antlr.jetbrains.st4plugin.parsing.STGParser; 21 | import org.antlr.v4.runtime.Parser; 22 | import org.antlr.v4.runtime.tree.ParseTree; 23 | import org.jetbrains.annotations.NotNull; 24 | 25 | public class STGroupParserDefinition implements ParserDefinition { 26 | 27 | public static final IFileElementType FILE = new IFileElementType(STGroupLanguage.INSTANCE); 28 | 29 | public STGroupParserDefinition() { 30 | STGroupTokenTypes.initIElementTypes(); 31 | } 32 | 33 | @NotNull 34 | @Override 35 | public Lexer createLexer(Project project) { 36 | STGLexer lexer = new STGLexer(null); 37 | return new ANTLRLexerAdaptor(STGroupLanguage.INSTANCE, lexer); 38 | } 39 | 40 | @Override 41 | public PsiParser createParser(Project project) { 42 | return new ANTLRParserAdaptor(STGroupLanguage.INSTANCE, new STGParser(null)) { 43 | @Override 44 | protected ParseTree parse(Parser parser, IElementType root) { 45 | return ((STGParser) parser).group(); 46 | } 47 | }; 48 | } 49 | 50 | @Override 51 | public IFileElementType getFileNodeType() { 52 | return FILE; 53 | } 54 | 55 | @NotNull 56 | @Override 57 | public TokenSet getCommentTokens() { 58 | return PSIElementTypeFactory.createTokenSet( 59 | STGroupLanguage.INSTANCE, 60 | STGLexer.COMMENT, 61 | STGLexer.LINE_COMMENT 62 | ); 63 | } 64 | 65 | @NotNull 66 | @Override 67 | public TokenSet getStringLiteralElements() { 68 | return PSIElementTypeFactory.createTokenSet( 69 | STGroupLanguage.INSTANCE, 70 | STGLexer.STRING 71 | ); 72 | } 73 | 74 | @NotNull 75 | @Override 76 | public PsiElement createElement(ASTNode node) { 77 | if (node.getElementType() == STGroupTokenTypes.getRuleElementType(STGParser.RULE_templateContent)) { 78 | return new TemplateContentElement(node); 79 | } 80 | 81 | return new ASTWrapperPsiElement(node); 82 | } 83 | 84 | @Override 85 | public PsiFile createFile(FileViewProvider viewProvider) { 86 | return new STGroupFile(viewProvider); 87 | } 88 | 89 | @NotNull 90 | @Override 91 | public TokenSet getWhitespaceTokens() { 92 | return PSIElementTypeFactory.createTokenSet( 93 | STGroupLanguage.INSTANCE, 94 | STGLexer.WS 95 | ); 96 | } 97 | 98 | @Override 99 | public SpaceRequirements spaceExistanceTypeBetweenTokens(ASTNode left, ASTNode right) { 100 | return SpaceRequirements.MAY; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/test/java/org/antlr/jetbrains/st4plugin/psi/TemplateContentLiteralTextEscaperTest.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.psi; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.intellij.openapi.util.TextRange; 5 | import org.antlr.intellij.adaptor.lexer.PSIElementTypeFactory; 6 | import org.antlr.jetbrains.st4plugin.STGroupLanguage; 7 | import org.antlr.jetbrains.st4plugin.parsing.STGLexer; 8 | import org.antlr.jetbrains.st4plugin.parsing.STGParser; 9 | import org.junit.BeforeClass; 10 | import org.junit.Test; 11 | import org.mockito.internal.stubbing.answers.ThrowsException; 12 | 13 | import static org.antlr.jetbrains.st4plugin.psi.STGroupTokenTypes.getTokenElementType; 14 | import static org.junit.Assert.assertEquals; 15 | import static org.mockito.Mockito.doReturn; 16 | import static org.mockito.Mockito.mock; 17 | 18 | public class TemplateContentLiteralTextEscaperTest { 19 | 20 | private TemplateContentLiteralTextEscaper escaper; 21 | 22 | @BeforeClass 23 | public static void beforeClass() { 24 | PSIElementTypeFactory.defineLanguageIElementTypes( 25 | STGroupLanguage.INSTANCE, 26 | STGLexer.tokenNames, 27 | STGParser.ruleNames 28 | ); 29 | } 30 | 31 | @Test 32 | public void testDecodeNoQuotes() { 33 | // Given 34 | String templateText = "\"some template\""; 35 | 36 | TemplateContentElement template = parse(templateText); 37 | escaper = new TemplateContentLiteralTextEscaper(template); 38 | 39 | TextRange rangeInsideHost = TextRange.create(1, templateText.length() - 1); 40 | 41 | StringBuilder outBuilder = new StringBuilder(); 42 | 43 | // When 44 | escaper.decode(rangeInsideHost, outBuilder); 45 | 46 | // Then 47 | assertEquals(rangeInsideHost.substring(templateText), outBuilder.toString()); 48 | 49 | assertEquals(1, escaper.getOffsetInHost(0, rangeInsideHost)); 50 | assertEquals(2, escaper.getOffsetInHost(1, rangeInsideHost)); 51 | assertEquals(3, escaper.getOffsetInHost(2, rangeInsideHost)); 52 | assertEquals(13, escaper.getOffsetInHost(12, rangeInsideHost)); 53 | } 54 | 55 | @Test 56 | public void testDecodeQuotes() { 57 | // Given 58 | String templateText = "\"format=\\\"cap\\\";\""; 59 | 60 | TemplateContentElement template = parse(templateText); 61 | escaper = new TemplateContentLiteralTextEscaper(template); 62 | 63 | TextRange rangeInsideHost = TextRange.create(1, templateText.length() - 1); 64 | 65 | StringBuilder outBuilder = new StringBuilder(); 66 | 67 | // When 68 | escaper.decode(rangeInsideHost, outBuilder); 69 | 70 | // Then 71 | assertEquals("format=\"cap\";", outBuilder.toString()); 72 | 73 | assertEquals(1, escaper.getOffsetInHost(0, rangeInsideHost)); // f 74 | assertEquals(2, escaper.getOffsetInHost(1, rangeInsideHost)); // o 75 | assertEquals(3, escaper.getOffsetInHost(2, rangeInsideHost)); // r 76 | assertEquals(9, escaper.getOffsetInHost(7, rangeInsideHost)); // " 77 | assertEquals(10, escaper.getOffsetInHost(8, rangeInsideHost)); // c 78 | assertEquals(14, escaper.getOffsetInHost(11, rangeInsideHost)); // " 79 | assertEquals(15, escaper.getOffsetInHost(12, rangeInsideHost)); // ; 80 | } 81 | 82 | private TemplateContentElement parse(String raw) { 83 | TemplateContentElement template = mock(TemplateContentElement.class, new ThrowsException(new UnsupportedOperationException())); 84 | ASTNode astNode = mock(ASTNode.class); 85 | 86 | doReturn(raw).when(template).getText(); 87 | doReturn(astNode).when(template).getNode(); 88 | doReturn(astNode).when(astNode).getFirstChildNode(); 89 | doReturn(getTokenElementType(STGLexer.STRING)).when(astNode).getElementType(); 90 | 91 | return template; 92 | } 93 | } -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/psi/STParserDefinition.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.psi; 2 | 3 | import com.intellij.extapi.psi.ASTWrapperPsiElement; 4 | import com.intellij.lang.ASTNode; 5 | import com.intellij.lang.ParserDefinition; 6 | import com.intellij.lang.PsiParser; 7 | import com.intellij.lexer.Lexer; 8 | import com.intellij.openapi.project.Project; 9 | import com.intellij.psi.FileViewProvider; 10 | import com.intellij.psi.PsiElement; 11 | import com.intellij.psi.PsiFile; 12 | import com.intellij.psi.tree.IElementType; 13 | import com.intellij.psi.tree.IFileElementType; 14 | import com.intellij.psi.tree.TokenSet; 15 | import org.antlr.intellij.adaptor.lexer.ANTLRLexerAdaptor; 16 | import org.antlr.intellij.adaptor.lexer.PSIElementTypeFactory; 17 | import org.antlr.intellij.adaptor.parser.ANTLRParserAdaptor; 18 | import org.antlr.jetbrains.st4plugin.STLanguage; 19 | import org.antlr.jetbrains.st4plugin.parsing.STLexer; 20 | import org.antlr.jetbrains.st4plugin.parsing.STParser; 21 | import org.antlr.v4.runtime.Parser; 22 | import org.antlr.v4.runtime.Token; 23 | import org.antlr.v4.runtime.tree.ParseTree; 24 | import org.jetbrains.annotations.NotNull; 25 | 26 | public class STParserDefinition implements ParserDefinition { 27 | 28 | public static final IFileElementType FILE = new IFileElementType(STLanguage.INSTANCE); 29 | 30 | public STParserDefinition() { 31 | STTokenTypes.initIElementTypes(); 32 | } 33 | 34 | @NotNull 35 | @Override 36 | public Lexer createLexer(Project project) { 37 | return new ANTLRLexerAdaptor(STLanguage.INSTANCE, new STLexer(null)) { 38 | @Override 39 | public void advance() { 40 | Token before = super.getCurrentToken(); 41 | super.advance(); 42 | Token after = super.getCurrentToken(); 43 | 44 | if (before != null && after != null) { 45 | if (after.getStartIndex() != before.getStopIndex() + 1) { 46 | System.err.println("Lexer gap between tokens\n" + before + "\n\nand\n\n" + after); 47 | } 48 | } 49 | } 50 | }; 51 | } 52 | 53 | @Override 54 | public PsiParser createParser(Project project) { 55 | return new ANTLRParserAdaptor(STLanguage.INSTANCE, new STParser(null)) { 56 | @Override 57 | protected ParseTree parse(Parser parser, IElementType root) { 58 | return ((STParser) parser).template(); 59 | } 60 | }; 61 | } 62 | 63 | @Override 64 | public IFileElementType getFileNodeType() { 65 | return FILE; 66 | } 67 | 68 | @NotNull 69 | @Override 70 | public TokenSet getCommentTokens() { 71 | return PSIElementTypeFactory.createTokenSet( 72 | STLanguage.INSTANCE, 73 | STLexer.TMPL_COMMENT 74 | ); 75 | } 76 | 77 | @NotNull 78 | @Override 79 | public TokenSet getStringLiteralElements() { 80 | return PSIElementTypeFactory.createTokenSet( 81 | STLanguage.INSTANCE, 82 | STLexer.STRING 83 | ); 84 | } 85 | 86 | @NotNull 87 | @Override 88 | public PsiElement createElement(ASTNode node) { 89 | return new ASTWrapperPsiElement(node); 90 | } 91 | 92 | @Override 93 | public PsiFile createFile(FileViewProvider viewProvider) { 94 | return new STFile(viewProvider); 95 | } 96 | 97 | @NotNull 98 | @Override 99 | public TokenSet getWhitespaceTokens() { 100 | return PSIElementTypeFactory.createTokenSet( 101 | STLanguage.INSTANCE, 102 | STLexer.VERT_WS, 103 | STLexer.HORZ_WS 104 | ); 105 | } 106 | 107 | @Override 108 | public SpaceRequirements spaceExistanceTypeBetweenTokens(ASTNode left, ASTNode right) { 109 | return SpaceRequirements.MAY; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/highlight/STColorSettingsPage.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.highlight; 2 | 3 | import com.intellij.openapi.editor.colors.TextAttributesKey; 4 | import com.intellij.openapi.fileTypes.SyntaxHighlighter; 5 | import com.intellij.openapi.options.colors.AttributesDescriptor; 6 | import com.intellij.openapi.options.colors.ColorDescriptor; 7 | import com.intellij.openapi.options.colors.ColorSettingsPage; 8 | import org.antlr.jetbrains.st4plugin.Icons; 9 | import org.jetbrains.annotations.Nls; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | import javax.swing.*; 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | 17 | public class STColorSettingsPage implements ColorSettingsPage { 18 | 19 | private static final AttributesDescriptor[] DESCRIPTORS = new AttributesDescriptor[]{ 20 | new AttributesDescriptor("Template Name", STGroupSyntaxHighlighter.TEMPLATE_NAME), 21 | new AttributesDescriptor("Template Parameter", STGroupSemanticHighlightAnnotator.TEMPLATE_PARAM), 22 | new AttributesDescriptor("String", STGroupSyntaxHighlighter.STRING), 23 | new AttributesDescriptor("Keyword", STGroupSyntaxHighlighter.KEYWORD), 24 | new AttributesDescriptor("Line Comment", STGroupSyntaxHighlighter.LINE_COMMENT), 25 | new AttributesDescriptor("Block Comment", STGroupSyntaxHighlighter.BLOCK_COMMENT), 26 | new AttributesDescriptor("Option", STSemanticHighlightAnnotator.OPTION) 27 | }; 28 | 29 | @Override 30 | public @Nullable Icon getIcon() { 31 | return Icons.STG_FILE; 32 | } 33 | 34 | @Override 35 | public @NotNull SyntaxHighlighter getHighlighter() { 36 | return new STGroupSyntaxHighlighter(); 37 | } 38 | 39 | @Override 40 | public @NotNull String getDemoText() { 41 | return "/**\n" + 42 | " * Multi line comment\n" + 43 | " */\n" + 44 | "\n" + 45 | "delimiters \"$\", \"$\"\n" + 46 | "\n" + 47 | "// single line comment\n" + 48 | "myMap ::= [\n" + 49 | " \"key\": \"value\",\n" + 50 | " default: key\n" + 51 | "]\n" + 52 | "\n" + 53 | "myTemplate(param1, param2) ::= <<\n" + 54 | " hello, world\n" + 55 | " \n" + 56 | " <if (param1)>\n" + 57 | " a\n" + 58 | " <elseif (true)>\n" + 59 | " b\n" + 60 | " <else>\n" + 61 | " <param2 =\", \">\n" + 62 | " <endif>\n" + 63 | ">>\n" + 64 | "\n" + 65 | "oneLiner(x) ::= \"hello, <x>\"\n"; 66 | } 67 | 68 | @Override 69 | public @Nullable Map getAdditionalHighlightingTagToDescriptorMap() { 70 | Map tagToDescriptor = new HashMap<>(); 71 | tagToDescriptor.put("param", STGroupSemanticHighlightAnnotator.TEMPLATE_PARAM); 72 | tagToDescriptor.put("keyword", STGroupSyntaxHighlighter.KEYWORD); 73 | tagToDescriptor.put("option", STSemanticHighlightAnnotator.OPTION); 74 | tagToDescriptor.put("comment", STGroupSyntaxHighlighter.BLOCK_COMMENT); 75 | return tagToDescriptor; 76 | } 77 | 78 | @Override 79 | public @NotNull AttributesDescriptor[] getAttributeDescriptors() { 80 | return DESCRIPTORS; 81 | } 82 | 83 | @Override 84 | public @NotNull ColorDescriptor[] getColorDescriptors() { 85 | return ColorDescriptor.EMPTY_ARRAY; 86 | } 87 | 88 | @Override 89 | public @NotNull @Nls( 90 | capitalization = Nls.Capitalization.Title 91 | ) String getDisplayName() { 92 | return "StringTemplate"; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/highlight/STSemanticHighlightAnnotator.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.highlight; 2 | 3 | import com.intellij.lang.ASTNode; 4 | import com.intellij.lang.annotation.AnnotationHolder; 5 | import com.intellij.lang.annotation.Annotator; 6 | import com.intellij.openapi.editor.DefaultLanguageHighlighterColors; 7 | import com.intellij.openapi.editor.colors.TextAttributesKey; 8 | import com.intellij.openapi.util.TextRange; 9 | import com.intellij.psi.PsiElement; 10 | import com.intellij.psi.tree.TokenSet; 11 | import com.intellij.psi.util.PsiTreeUtil; 12 | import org.antlr.jetbrains.st4plugin.parsing.STLexer; 13 | import org.antlr.jetbrains.st4plugin.parsing.STParser; 14 | import org.antlr.jetbrains.st4plugin.psi.STTokenTypes; 15 | import org.jetbrains.annotations.NotNull; 16 | 17 | import static com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey; 18 | import static org.antlr.jetbrains.st4plugin.psi.STTokenTypes.getRuleElementType; 19 | import static org.antlr.jetbrains.st4plugin.psi.STTokenTypes.getTokenElementType; 20 | 21 | /** 22 | * Semantic highlighting for .st files. 23 | */ 24 | public class STSemanticHighlightAnnotator implements Annotator { 25 | 26 | private static final TextAttributesKey ST_TAG = createTextAttributesKey("ST_TAG"); 27 | public static final TextAttributesKey OPTION = createTextAttributesKey("ST_OPTION", DefaultLanguageHighlighterColors.INSTANCE_METHOD); 28 | 29 | @Override 30 | public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) { 31 | if (element.getNode().getElementType() == STTokenTypes.getTokenElementType(STLexer.ID)) { 32 | if (isPrimary(element) || isSubtemplate(element)) { 33 | holder.createInfoAnnotation(element, null) 34 | .setTextAttributes(STGroupSemanticHighlightAnnotator.TEMPLATE_PARAM); 35 | } else if (isOptionId(element)) { 36 | holder.createInfoAnnotation(element, null) 37 | .setTextAttributes(OPTION); 38 | } else if (isCall(element)) { 39 | holder.createInfoAnnotation(element, null) 40 | .setTextAttributes(STGroupSyntaxHighlighter.TEMPLATE_NAME); 41 | } 42 | } 43 | 44 | if (isTag(element)) { 45 | // use a white/dark background instead of the default green from injected languages 46 | holder.createInfoAnnotation(element, null) 47 | .setTextAttributes(ST_TAG); 48 | } 49 | 50 | if (element.getNode().getElementType() == getRuleElementType(STParser.RULE_ifstat)) { 51 | for (ASTNode ldelim : element.getNode().getChildren(TokenSet.create(getTokenElementType(STLexer.LDELIM)))) { 52 | ASTNode rdelim = element.getNode().findChildByType(getTokenElementType(STLexer.RDELIM), ldelim); 53 | 54 | if (rdelim != null) { 55 | // use a white/dark background instead of the default green from injected languages 56 | holder.createInfoAnnotation(TextRange.create(ldelim.getStartOffset(), rdelim.getStartOffset() + rdelim.getTextLength()), null) 57 | .setTextAttributes(ST_TAG); 58 | } 59 | } 60 | } 61 | } 62 | 63 | private boolean isSubtemplate(@NotNull PsiElement element) { 64 | return element.getParent().getNode().getElementType() == getRuleElementType(STParser.RULE_subtemplate); 65 | } 66 | 67 | private boolean isTag(@NotNull PsiElement element) { 68 | return element.getNode().getElementType() == STTokenTypes.getRuleElementType(STParser.RULE_exprTag); 69 | } 70 | 71 | private boolean isPrimary(@NotNull PsiElement element) { 72 | return element.getParent().getNode().getElementType() == getRuleElementType(STParser.RULE_primary); 73 | } 74 | 75 | private boolean isCall(PsiElement element) { 76 | PsiElement nextVisibleLeaf = PsiTreeUtil.nextVisibleLeaf(element); 77 | 78 | return nextVisibleLeaf != null && nextVisibleLeaf.getNode().getElementType() == STTokenTypes.getTokenElementType(STLexer.LPAREN); 79 | } 80 | 81 | private boolean isOptionId(@NotNull PsiElement element) { 82 | return element.getParent().getNode().getElementType() == getRuleElementType(STParser.RULE_option); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/antlr/STGLexer.g4: -------------------------------------------------------------------------------- 1 | /* 2 | * [The "BSD license"] 3 | * Copyright (c) 2011-2014 Terence Parr 4 | * Copyright (c) 2015 Gerald Rosenberg 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. The name of the author may not be used to endorse or promote products 16 | * derived from this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /** 31 | * A grammar for StringTemplate v4 implemented using Antlr v4 syntax 32 | * 33 | * Modified 2015.06.16 gbr 34 | * -- update for compatibility with Antlr v4.5 35 | * -- use imported standard fragments 36 | */ 37 | 38 | lexer grammar STGLexer; 39 | 40 | import LexBasic; // Standard set of fragments 41 | 42 | // ------------------------------------------------------------------------------ 43 | // mode default 44 | 45 | COMMENT : BlockComment -> channel(HIDDEN) ; 46 | LINE_COMMENT : LineComment -> channel(HIDDEN) ; 47 | 48 | WS : [ \r\n\t]+ -> channel(HIDDEN) ; 49 | 50 | STRING_START : DQuote -> more, pushMode(STRING_MODE) ; 51 | ANON_TEMPLATE : LBrace ('\\}'|~'}')* RBrace ; 52 | BIGSTRING_START : LDAngle -> more, pushMode(BIGSTRING_MODE) ; 53 | BIGSTRING_NO_NL_START : LPct -> more, pushMode(BIGSTRING_NO_NL_MODE) ; 54 | 55 | 56 | // ----------------------------------- 57 | // Symbols 58 | 59 | TMPL_ASSIGN : TmplAssign ; 60 | ASSIGN : Equal ; 61 | 62 | DOT : Dot ; 63 | COMMA : Comma ; 64 | COLON : Colon ; 65 | SEMI : Semi ; 66 | LPAREN : LParen ; 67 | RPAREN : RParen ; 68 | LBRACK : LBrack ; 69 | RBRACK : RBrack ; 70 | AT : At ; 71 | TRUE : True ; 72 | FALSE : False ; 73 | ELLIPSIS : Ellipsis ; 74 | 75 | // ----------------------------------- 76 | // Key words 77 | 78 | DELIMITERS : 'delimiters' ; 79 | IMPORT : 'import' ; 80 | DEFAULT : 'default' ; 81 | KEY : 'key' ; 82 | 83 | GROUP : 'group' ; // for compatibility with V3 syntax 84 | IMPLEMENTS : 'implements' ; // for compatibility with V3 syntax 85 | 86 | ID : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'-'|'_')* ; 87 | 88 | // ----------------------------------- 89 | // Grammar specific fragments 90 | 91 | fragment TmplAssign : '::=' ; 92 | fragment LPct : '<%' ; 93 | fragment RPct : '%>' ; 94 | fragment LDAngle : LShift ; 95 | fragment RDAngle : RShift ; 96 | 97 | ERRCHAR : . -> channel (HIDDEN) ; 98 | 99 | mode STRING_MODE; 100 | 101 | STRING_ESC : '\\"' {setText(getText()+"\"");} -> more ; 102 | STRING_NL : '\n' -> more ; // match but it should be an error 103 | STRING : '"' -> popMode ; 104 | EOF_STRING : EOF -> popMode ; 105 | STRING_TEXT : . -> more; 106 | 107 | mode BIGSTRING_MODE; 108 | 109 | BIGSTRING_ESC : '\\>' -> more ; 110 | BIGSTRING : '>>' -> popMode ; 111 | EOF_BIGSTRING : EOF -> popMode ; 112 | BIGSTRING_TEXT : . -> more; 113 | 114 | mode BIGSTRING_NO_NL_MODE; 115 | 116 | BIGSTRING_NO_NL_ESC : '\\%' -> more; 117 | BIGSTRING_NO_NL : '%>' -> popMode ; 118 | EOF_BIGSTRING_NO_NL : EOF -> popMode ; 119 | BIGSTRING_NO_NL_TEXT: . -> more; 120 | 121 | 122 | -------------------------------------------------------------------------------- /src/main/antlr/STLexer.g4: -------------------------------------------------------------------------------- 1 | /* [The "BSD license"] 2 | * Copyright (c) 2011-2014 Terence Parr 3 | * Copyright (c) 2015 Gerald Rosenberg 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. The name of the author may not be used to endorse or promote products 15 | * derived from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | /* Antlr grammar for StringTemplate v4. 30 | * 31 | * Modified 2015.06.16 gbr 32 | * -- update for compatibility with Antlr v4.5 33 | * -- use imported standard fragments 34 | */ 35 | 36 | lexer grammar STLexer; 37 | 38 | options { 39 | superClass = LexerAdaptor ; 40 | } 41 | 42 | import LexBasic; // Standard set of fragments 43 | 44 | channels { 45 | OFF_CHANNEL // non-default channel for whitespace and comments 46 | } 47 | 48 | tokens { 49 | HORZ_WS, VERT_WS 50 | } 51 | 52 | // ----------------------------------- 53 | // default mode = Outside 54 | 55 | TMPL_COMMENT : TmplComment -> channel(OFF_CHANNEL) ; 56 | 57 | ESCAPE : . { isLDelim() }? EscSeq . { isRDelim() }? ; // self contained 58 | LDELIM : . { isLDelim() }? -> mode(Inside) ; // switch mode to inside 59 | RBRACE : RBrace { endsSubTemplate() }? ; // conditional switch to inside 60 | 61 | TEXT : . { adjText(); } ; // have to handle weird terminals 62 | 63 | 64 | // ----------------------------------- 65 | mode Inside ; 66 | 67 | INS_HORZ_WS : Hws+ -> type(HORZ_WS), channel(OFF_CHANNEL) ; 68 | INS_VERT_WS : Vws+ -> type(VERT_WS), channel(OFF_CHANNEL) ; 69 | 70 | LBRACE : LBrace { startSubTemplate(); } ; 71 | RDELIM : . { isRDelim() }? -> mode(DEFAULT_MODE) ; 72 | 73 | STRING : DQuoteLiteral ; 74 | 75 | IF : 'if' ; 76 | ELSEIF : 'elseif' ; 77 | ELSE : 'else' ; 78 | ENDIF : 'endif' ; 79 | SUPER : 'super' ; 80 | END : '@end' ; 81 | 82 | TRUE : True ; 83 | FALSE : False ; 84 | 85 | INS_ID : NameStartChar NameChar* -> type(ID); 86 | 87 | AT : At ; 88 | ELLIPSIS : Ellipsis ; 89 | DOT : Dot ; 90 | COMMA : Comma ; 91 | COLON : Colon ; 92 | SEMI : Semi ; 93 | AND : And ; 94 | OR : Or ; 95 | LPAREN : LParen ; 96 | RPAREN : RParen ; 97 | LBRACK : LBrack ; 98 | RBRACK : RBrack ; 99 | EQUALS : Equal ; 100 | BANG : Bang ; 101 | 102 | INS_EOF : EOF; 103 | 104 | // ----------------------------------- 105 | // Unknown content in mode Inside 106 | INS_ERR_CHAR : . -> channel(HIDDEN) ; 107 | 108 | 109 | // ----------------------------------- 110 | mode SubTemplate ; 111 | 112 | SUB_HORZ_WS : Hws+ -> type(HORZ_WS), channel(OFF_CHANNEL) ; 113 | SUB_VERT_WS : Vws+ -> type(VERT_WS), channel(OFF_CHANNEL) ; 114 | 115 | ID : NameStartChar NameChar* ; 116 | SUB_COMMA : Comma -> type(COMMA) ; 117 | PIPE : Pipe -> mode(DEFAULT_MODE) ; 118 | 119 | 120 | // ----------------------------------- 121 | // Grammar specific fragments 122 | 123 | fragment TmplComment : LTmplMark .*? RTmplMark ; 124 | 125 | fragment LTmplMark : . { isLTmplComment() }? Bang ; 126 | fragment RTmplMark : Bang . { isRTmplComment() }? ; 127 | 128 | SUB_EOF : EOF; 129 | 130 | SUB_ERR_CHAR : . -> channel(HIDDEN) ; 131 | -------------------------------------------------------------------------------- /src/test/java/org/antlr/jetbrains/st4plugin/structview/STGroupTemplateDefItemPresentationTest.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.structview; 2 | 3 | import static org.antlr.jetbrains.st4plugin.psi.STGroupTokenTypes.getRuleElementType; 4 | 5 | import com.intellij.extapi.psi.ASTWrapperPsiElement; 6 | import com.intellij.psi.PsiElement; 7 | import com.intellij.psi.PsiFile; 8 | import com.intellij.psi.PsiFileFactory; 9 | import com.intellij.testFramework.fixtures.CodeInsightFixtureTestCase; 10 | import javax.swing.Icon; 11 | import org.antlr.jetbrains.st4plugin.Icons; 12 | import org.antlr.jetbrains.st4plugin.STGroupFileType; 13 | import org.antlr.jetbrains.st4plugin.parsing.STGParser; 14 | import org.jetbrains.annotations.NotNull; 15 | 16 | public class STGroupTemplateDefItemPresentationTest extends CodeInsightFixtureTestCase { 17 | 18 | public void testIssue37() { 19 | // Given 20 | ASTWrapperPsiElement template = parseTemplate("A() ::= "); 21 | 22 | STGroupTemplateDefItemPresentation presentation = new STGroupTemplateDefItemPresentation(template); 23 | 24 | // When 25 | Icon icon = presentation.getIcon(false); 26 | 27 | // Then 28 | assertSame(template.getNode().getElementType(), getRuleElementType(STGParser.RULE_template)); 29 | assertEquals(Icons.BIGSTRING, icon); 30 | } 31 | 32 | public void testDict() { 33 | // Given 34 | ASTWrapperPsiElement template = parseTemplate("dict ::= [] "); 35 | 36 | STGroupTemplateDefItemPresentation presentation = new STGroupTemplateDefItemPresentation(template); 37 | 38 | // When 39 | Icon icon = presentation.getIcon(false); 40 | String text = presentation.getPresentableText(); 41 | 42 | // Then 43 | assertEquals(Icons.DICT, icon); 44 | assertEquals("dict", text); 45 | } 46 | 47 | public void testString() { 48 | // Given 49 | ASTWrapperPsiElement template = parseTemplate("stringTpl() ::= \"\" "); 50 | 51 | STGroupTemplateDefItemPresentation presentation = new STGroupTemplateDefItemPresentation(template); 52 | 53 | // When 54 | Icon icon = presentation.getIcon(false); 55 | String text = presentation.getPresentableText(); 56 | 57 | // Then 58 | assertEquals(Icons.STRING, icon); 59 | assertEquals("stringTpl", text); 60 | } 61 | 62 | public void testBigString() { 63 | // Given 64 | ASTWrapperPsiElement template = parseTemplate("bigStringTpl() ::= <<>> "); 65 | 66 | STGroupTemplateDefItemPresentation presentation = new STGroupTemplateDefItemPresentation(template); 67 | 68 | // When 69 | Icon icon = presentation.getIcon(false); 70 | String text = presentation.getPresentableText(); 71 | 72 | // Then 73 | assertEquals(Icons.BIGSTRING, icon); 74 | assertEquals("bigStringTpl", text); 75 | } 76 | 77 | public void testBigStringNoNlWithParams() { 78 | // Given 79 | ASTWrapperPsiElement template = parseTemplate("bigStringNoNl(foo, bar) ::= <%%> "); 80 | 81 | STGroupTemplateDefItemPresentation presentation = new STGroupTemplateDefItemPresentation(template); 82 | 83 | // When 84 | Icon icon = presentation.getIcon(false); 85 | String text = presentation.getPresentableText(); 86 | 87 | // Then 88 | assertEquals(Icons.BIGSTRING_NONL, icon); 89 | assertEquals("bigStringNoNl(foo, bar)", text); 90 | } 91 | 92 | public void testTemplateAlias() { 93 | // Given 94 | ASTWrapperPsiElement template = parseTemplate("foo ::= bar"); 95 | 96 | STGroupTemplateDefItemPresentation presentation = new STGroupTemplateDefItemPresentation(template); 97 | 98 | // When 99 | Icon icon = presentation.getIcon(false); 100 | String text = presentation.getPresentableText(); 101 | 102 | // Then 103 | assertEquals(Icons.BIGSTRING, icon); 104 | assertEquals("foo", text); 105 | } 106 | 107 | @NotNull 108 | private ASTWrapperPsiElement parseTemplate(String content) { 109 | PsiFile file = PsiFileFactory.getInstance(myFixture.getProject()) 110 | .createFileFromText("a.stg", STGroupFileType.INSTANCE, content); 111 | 112 | PsiElement template = file.getFirstChild().getFirstChild(); 113 | 114 | return (ASTWrapperPsiElement) template; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | org.antlr.jetbrains.st4plugin 3 | StringTemplate v4 4 | will be replaced by gradle 5 | ANTLR Project 6 | 7 | 9 | This plugin is for StringTemplate v4 .stg/.st files. It works with 10 | IntelliJ IDEA 2022.2 and newer. It should work in other IntelliJ-based IDEs. 11 |

12 | 13 |

Github source

14 | ]]>
15 | 16 | 18 |
  • Fixed compatibility with IntelliJ 2022.3.x (#41)
  • 19 |
  • Fixed compatibility with Rider 2022.3.x (#42)
  • 20 |
  • Updated to latest versions of ANTLR and StringTemplate
  • 21 | 22 | ]]> 23 |
    24 | 25 | 26 | 27 | 28 | 30 | 31 | com.intellij.modules.lang 32 | org.intellij.intelliLang 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 49 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 |
    74 | -------------------------------------------------------------------------------- /src/main/antlr/STParser.g4: -------------------------------------------------------------------------------- 1 | /* [The "BSD license"] 2 | * Copyright (c) 2011-2014 Terence Parr 3 | * Copyright (c) 2015 Gerald Rosenberg 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. The name of the author may not be used to endorse or promote products 15 | * derived from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | /* Antlr grammar for StringTemplate v4. 30 | * 31 | * Modified 2015.06.21 gbr 32 | * -- use imported standard fragments 33 | */ 34 | 35 | parser grammar STParser; 36 | 37 | options { 38 | language=Java; 39 | tokenVocab=STLexer; 40 | } 41 | 42 | template 43 | : elements 44 | ; 45 | 46 | elements 47 | : element* 48 | ; 49 | 50 | element 51 | : singleElement 52 | | compoundElement 53 | ; 54 | 55 | singleElement 56 | : exprTag 57 | | (TEXT | ESCAPE)+ 58 | ; 59 | 60 | compoundElement 61 | : ifstat 62 | | region 63 | ; 64 | 65 | exprTag 66 | : LDELIM mapExpr ( SEMI exprOptions )? RDELIM 67 | ; 68 | 69 | region 70 | : LDELIM AT ID RDELIM 71 | elements 72 | LDELIM END RDELIM 73 | ; 74 | 75 | subtemplate 76 | : LBRACE ( ID ( COMMA ID )* PIPE )? elements RBRACE 77 | ; 78 | 79 | ifstat 80 | : LDELIM IF LPAREN conditional RPAREN RDELIM 81 | elements 82 | ( LDELIM ELSEIF LPAREN conditional RPAREN RDELIM elements )* 83 | ( LDELIM ELSE RDELIM elements )? 84 | LDELIM ENDIF RDELIM 85 | ; 86 | 87 | conditional 88 | : andConditional ( OR andConditional )* 89 | ; 90 | 91 | andConditional 92 | : notConditional ( AND notConditional )* 93 | ; 94 | 95 | notConditional 96 | : BANG notConditional 97 | | memberExpr 98 | ; 99 | 100 | notConditionalExpr 101 | : ID ( DOT ID 102 | | DOT LPAREN mapExpr RPAREN 103 | )* 104 | ; 105 | 106 | exprOptions 107 | : option ( COMMA option )* 108 | ; 109 | 110 | option 111 | : ID ( EQUALS expr )? 112 | ; 113 | 114 | expr 115 | : memberExpr ( COLON mapTemplateRef )? 116 | ; 117 | 118 | // more complicated than necessary to avoid backtracking, 119 | // which ruins error handling 120 | mapExpr 121 | : memberExpr ( ( COMMA memberExpr )+ COLON mapTemplateRef )? 122 | ( COLON mapTemplateRef ( COMMA mapTemplateRef )* )* 123 | ; 124 | 125 | memberExpr 126 | : includeExpr 127 | ( DOT ID 128 | | DOT LPAREN mapExpr RPAREN 129 | )* 130 | ; 131 | 132 | // expr:template(args) apply template to expr 133 | // expr:{arg | ...} apply subtemplate to expr 134 | // expr:(e)(args) convert e to a string template name and apply to expr 135 | mapTemplateRef 136 | : ID LPAREN args? RPAREN 137 | | subtemplate 138 | | LPAREN mapExpr RPAREN LPAREN argExprList? RPAREN 139 | ; 140 | 141 | includeExpr 142 | : ID LPAREN mapExpr? RPAREN 143 | | SUPER DOT ID LPAREN args? RPAREN 144 | | ID LPAREN args? RPAREN 145 | | AT SUPER DOT ID LPAREN RPAREN 146 | | AT ID LPAREN RPAREN 147 | | primary 148 | ; 149 | 150 | primary 151 | : ID 152 | | STRING 153 | | TRUE 154 | | FALSE 155 | | subtemplate 156 | | list 157 | | LPAREN conditional RPAREN 158 | | LPAREN mapExpr RPAREN ( LPAREN argExprList? RPAREN )? 159 | ; 160 | 161 | list 162 | : LBRACK argExprList? RBRACK 163 | ; 164 | 165 | args 166 | : argExprList 167 | | namedArg ( COMMA namedArg )* ( COMMA ELLIPSIS )? 168 | | ELLIPSIS 169 | ; 170 | 171 | argExprList 172 | : expr ( COMMA expr )* 173 | ; 174 | 175 | namedArg 176 | : ID EQUALS expr 177 | ; 178 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/folding/STGroupFoldingBuilder.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.folding; 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.util.TextRange; 8 | import com.intellij.psi.PsiElement; 9 | import com.intellij.psi.tree.TokenSet; 10 | import com.intellij.psi.util.PsiTreeUtil; 11 | import org.antlr.jetbrains.st4plugin.parsing.STGLexer; 12 | import org.antlr.jetbrains.st4plugin.parsing.STGParser; 13 | import org.antlr.jetbrains.st4plugin.psi.STGroupFile; 14 | import org.jetbrains.annotations.NotNull; 15 | 16 | import java.util.List; 17 | 18 | import static org.antlr.jetbrains.st4plugin.psi.STGroupTokenTypes.getRuleElementType; 19 | import static org.antlr.jetbrains.st4plugin.psi.STGroupTokenTypes.getTokenElementType; 20 | 21 | public class STGroupFoldingBuilder extends CustomFoldingBuilder { 22 | 23 | @Override 24 | protected void buildLanguageFoldRegions(@NotNull List descriptors, 25 | @NotNull PsiElement root, 26 | @NotNull Document document, 27 | boolean quick) { 28 | if (!(root instanceof STGroupFile)) { 29 | return; 30 | } 31 | 32 | foldTemplates(descriptors, root); 33 | foldDicts(descriptors, root); 34 | foldComments(descriptors, root); 35 | } 36 | 37 | private void foldTemplates(List descriptors, PsiElement root) { 38 | PsiTreeUtil.processElements(root, element -> { 39 | if (element.getNode().getElementType() == getRuleElementType(STGParser.RULE_templateContent)) { 40 | ASTNode bigString = element.getNode().findChildByType(TokenSet.create( 41 | getTokenElementType(STGLexer.BIGSTRING), 42 | getTokenElementType(STGLexer.BIGSTRING_NO_NL) 43 | )); 44 | 45 | if (bigString != null) { 46 | descriptors.add(new FoldingDescriptor(bigString, bigString.getTextRange())); 47 | } 48 | } else if (element.getNode().getElementType() == getRuleElementType(STGParser.RULE_formalArg)) { 49 | ASTNode template = element.getNode().findChildByType(getTokenElementType(STGLexer.ANON_TEMPLATE)); 50 | 51 | if (template != null) { 52 | descriptors.add(new FoldingDescriptor(template, template.getTextRange())); 53 | } 54 | } 55 | 56 | return true; 57 | }); 58 | } 59 | 60 | private void foldDicts(List descriptors, PsiElement root) { 61 | PsiTreeUtil.processElements(root, element -> { 62 | if (element.getNode().getElementType() == getRuleElementType(STGParser.RULE_dict)) { 63 | ASTNode lbrack = element.getNode().findChildByType(getTokenElementType(STGLexer.LBRACK)); 64 | ASTNode rbrack = element.getNode().findChildByType(getTokenElementType(STGLexer.RBRACK)); 65 | 66 | if (lbrack != null && rbrack != null) { 67 | TextRange range = lbrack.getTextRange().union(rbrack.getTextRange()); 68 | descriptors.add(new FoldingDescriptor(element, range)); 69 | } 70 | } 71 | 72 | return true; 73 | }); 74 | } 75 | 76 | private void foldComments(List descriptors, PsiElement root) { 77 | PsiTreeUtil.processElements(root, element -> { 78 | if (element.getNode().getElementType() == getTokenElementType(STGLexer.COMMENT)) { 79 | descriptors.add(new FoldingDescriptor(element, element.getTextRange())); 80 | } 81 | 82 | return true; 83 | }); 84 | 85 | } 86 | 87 | @Override 88 | protected String getLanguagePlaceholderText(@NotNull ASTNode node, @NotNull TextRange range) { 89 | if (node.getElementType() == getTokenElementType(STGLexer.BIGSTRING)) { 90 | return "<<...>>"; 91 | } else if (node.getElementType() == getTokenElementType(STGLexer.BIGSTRING_NO_NL)) { 92 | return "<%...%>"; 93 | } else if (node.getElementType() == getTokenElementType(STGLexer.ANON_TEMPLATE)) { 94 | return "{...}"; 95 | } else if (node.getElementType() == getRuleElementType(STGParser.RULE_dict)) { 96 | return "[...]"; 97 | } else if (node.getElementType() == getTokenElementType(STGLexer.COMMENT)) { 98 | return "/*...*/"; 99 | } 100 | return "..."; 101 | } 102 | 103 | @Override 104 | protected boolean isRegionCollapsedByDefault(@NotNull ASTNode node) { 105 | return false; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/org/antlr/jetbrains/st4plugin/parsing/LexerAdaptor.java: -------------------------------------------------------------------------------- 1 | package org.antlr.jetbrains.st4plugin.parsing; 2 | 3 | import com.intellij.psi.PsiLanguageInjectionHost; 4 | import org.antlr.v4.runtime.CharStream; 5 | import org.antlr.v4.runtime.CommonToken; 6 | import org.antlr.v4.runtime.IntStream; 7 | import org.antlr.v4.runtime.Lexer; 8 | import org.antlr.v4.runtime.Token; 9 | 10 | public abstract class LexerAdaptor extends Lexer { 11 | 12 | public static final char DELIMITERS_PREFIX = '\u0001'; 13 | 14 | private final java.util.Queue queue = new java.util.LinkedList<>(); 15 | 16 | private char lDelim = '<'; 17 | private char rDelim = '>'; 18 | 19 | private int subtemplateDepth; 20 | 21 | public LexerAdaptor(CharStream input) { 22 | super(input); 23 | } 24 | 25 | @Override 26 | public Token nextToken() { 27 | if (_input.index() == 0 && _input.LA(1) == DELIMITERS_PREFIX) { 28 | return lexDelimitersPrefix(); 29 | } 30 | 31 | if (!queue.isEmpty()) { 32 | return queue.poll(); 33 | } 34 | 35 | Token next = super.nextToken(); 36 | 37 | return next.getType() == STLexer.TEXT ? mergeConsecutiveTextTokens(next) : next; 38 | } 39 | 40 | private Token mergeConsecutiveTextTokens(Token next) { 41 | StringBuilder builder = new StringBuilder(); 42 | Token startToken = next; 43 | 44 | while (next.getType() == STLexer.TEXT) { 45 | builder.append(next.getText()); 46 | next = super.nextToken(); 47 | } 48 | 49 | // The `next` will _not_ be a TEXT-token, store it in 50 | // the queue to return the next time! 51 | queue.offer(next); 52 | 53 | CommonToken token = new CommonToken(startToken); 54 | token.setStopIndex(startToken.getStartIndex() + builder.length() - 1); 55 | 56 | return token; 57 | } 58 | 59 | /** 60 | * @see org.antlr.jetbrains.st4plugin.psi.STLanguageInjector#detectDelimiters(PsiLanguageInjectionHost) 61 | */ 62 | private Token lexDelimitersPrefix() { 63 | int _lDelim = _input.LA(2); 64 | int _rDelim = _input.LA(3); 65 | 66 | if (_lDelim != -1 && _rDelim != -1) { 67 | setDelimiters((char) _lDelim, (char) _rDelim); 68 | 69 | // Consume the prefix and the delimiters 70 | _input.consume(); 71 | _input.consume(); 72 | _input.consume(); 73 | 74 | return getTokenFactory().create(_tokenFactorySourcePair, STLexer.HORZ_WS, "xxx", HIDDEN, 0, 2, 1, 0); 75 | } 76 | 77 | return super.nextToken(); 78 | } 79 | 80 | public void startSubTemplate() { 81 | subtemplateDepth++; 82 | 83 | // look for "{ args ID (',' ID)* '|' ..." 84 | if (isSubTemplateWithArgs()) { 85 | mode(STLexer.SubTemplate); 86 | } else { 87 | mode(STLexer.DEFAULT_MODE); 88 | } 89 | } 90 | 91 | private boolean isSubTemplateWithArgs() { 92 | int position = _input.index(); 93 | int mark = _input.mark(); 94 | 95 | boolean isSubTemplateWithArgs = matchSubTemplateWithArgs(); 96 | 97 | _input.seek(position); 98 | _input.release(mark); 99 | 100 | return isSubTemplateWithArgs; 101 | } 102 | 103 | private boolean matchSubTemplateWithArgs() { 104 | matchWs(); 105 | if (!matchId()) { 106 | return false; 107 | } 108 | matchWs(); 109 | 110 | while (_input.LA(1) == ',') { 111 | _input.consume(); 112 | matchWs(); 113 | if (!matchId()) { 114 | return false; 115 | } 116 | matchWs(); 117 | } 118 | 119 | return _input.LA(1) == '|'; 120 | } 121 | 122 | private boolean matchId() { 123 | boolean isId = false; 124 | 125 | while (isIDLetter((char) _input.LA(1))) { 126 | _input.consume(); 127 | isId = true; 128 | } 129 | 130 | return isId; 131 | } 132 | 133 | private void matchWs() { 134 | int c = _input.LA(1); 135 | while (c == ' ' || c == '\t' || c == '\n' || c == '\r') { 136 | _input.consume(); 137 | c = _input.LA(1); 138 | } 139 | } 140 | 141 | // if last RBrace, continue with mode Outside 142 | public boolean endsSubTemplate() { 143 | if (subtemplateDepth > 0) { 144 | subtemplateDepth--; 145 | mode(1); // STLexer.Inside 146 | 147 | return true; 148 | } 149 | 150 | return false; 151 | } 152 | 153 | public void setDelimiters(char lDelim, char rDelim) { 154 | this.lDelim = lDelim; 155 | this.rDelim = rDelim; 156 | } 157 | 158 | public boolean isLDelim() { 159 | return lDelim == _input.LA(-1); 160 | } 161 | 162 | public boolean isRDelim() { 163 | return rDelim == _input.LA(-1); 164 | } 165 | 166 | public boolean isLTmplComment() { 167 | return isLDelim() && _input.LA(1) == '!'; 168 | } 169 | 170 | public boolean isRTmplComment() { 171 | return isRDelim() && _input.LA(-2) == '!'; 172 | } 173 | 174 | public void adjText() { 175 | int c1 = _input.LA(-1); 176 | if (c1 == '\\') { 177 | int c2 = _input.LA(1); 178 | if (c2 == '\\') { 179 | _input.consume(); // convert \\ to \ 180 | } else if (c2 == lDelim || c2 == '}') { 181 | _input.consume(); 182 | } 183 | } 184 | } 185 | 186 | public Token newTokenFromPreviousChar(int ttype) { 187 | return _factory.create(_tokenFactorySourcePair, ttype, _text, _channel, getCharIndex() - 1, getCharIndex() - 1, 188 | _tokenStartLine, _tokenStartCharPositionInLine); 189 | } 190 | 191 | @Override 192 | public void setInputStream(IntStream input) { 193 | queue.clear(); 194 | super.setInputStream(input); 195 | } 196 | 197 | private static boolean isIDLetter(char c) { 198 | return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '-' || c == '_'; 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /src/main/antlr/LexBasic.g4: -------------------------------------------------------------------------------- 1 | /* 2 | * [The "BSD license"] 3 | * Copyright (c) 2014-2015 Gerald Rosenberg 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. The name of the author may not be used to endorse or promote products 16 | * derived from this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /** 31 | * A generally reusable set of fragments for import in to Lexer grammars. 32 | * 33 | * Modified 2015.06.16 gbr - 34 | * -- generalized for inclusion into the ANTLRv4 grammar distribution 35 | * 36 | */ 37 | 38 | lexer grammar LexBasic; 39 | 40 | import LexUnicode; // Formal set of Unicode ranges 41 | 42 | // ====================================================== 43 | // Lexer fragments 44 | // 45 | 46 | 47 | // ----------------------------------- 48 | // Whitespace & Comments 49 | 50 | fragment Ws : Hws | Vws ; 51 | fragment Hws : [ \t] ; 52 | fragment Vws : [\r\n\f] ; 53 | 54 | fragment DocComment : '/**' .*? ('*/' | EOF) ; 55 | fragment BlockComment : '/*' .*? ('*/' | EOF) ; 56 | 57 | fragment LineComment : '//' ~[\r\n]* ; 58 | fragment LineCommentExt : '//' ~'\n'* ( '\n' Hws* '//' ~'\n'* )* ; 59 | 60 | 61 | // ----------------------------------- 62 | // Escapes 63 | 64 | // Any kind of escaped character that we can embed within ANTLR literal strings. 65 | fragment EscSeq 66 | : Esc 67 | ( [btnfr"'\\] // The standard escaped character set such as tab, newline, etc. 68 | | UnicodeEsc // A Unicode escape sequence 69 | | . // Invalid escape character 70 | | EOF // Incomplete at EOF 71 | ) 72 | ; 73 | 74 | fragment EscAny 75 | : Esc . 76 | ; 77 | 78 | fragment UnicodeEsc 79 | : 'u' (HexDigit (HexDigit (HexDigit HexDigit?)?)?)? 80 | ; 81 | 82 | fragment OctalEscape 83 | : OctalDigit 84 | | OctalDigit OctalDigit 85 | | [0-3] OctalDigit OctalDigit 86 | ; 87 | 88 | 89 | // ----------------------------------- 90 | // Numerals 91 | 92 | fragment HexNumeral 93 | : '0' [xX] HexDigits 94 | ; 95 | 96 | fragment OctalNumeral 97 | : '0' '_' OctalDigits 98 | ; 99 | 100 | fragment DecimalNumeral 101 | : '0' 102 | | [1-9] DecDigit* 103 | ; 104 | 105 | fragment BinaryNumeral 106 | : '0' [bB] BinaryDigits 107 | ; 108 | 109 | 110 | // ----------------------------------- 111 | // Digits 112 | 113 | fragment HexDigits : HexDigit+ ; 114 | fragment DecDigits : DecDigit+ ; 115 | fragment OctalDigits : OctalDigit+ ; 116 | fragment BinaryDigits : BinaryDigit+ ; 117 | 118 | fragment HexDigit : [0-9a-fA-F] ; 119 | fragment DecDigit : [0-9] ; 120 | fragment OctalDigit : [0-7] ; 121 | fragment BinaryDigit : [01] ; 122 | 123 | 124 | // ----------------------------------- 125 | // Literals 126 | 127 | fragment BoolLiteral : True | False ; 128 | 129 | fragment CharLiteral : SQuote ( EscSeq | ~['\r\n\\] ) SQuote ; 130 | fragment SQuoteLiteral : SQuote ( EscSeq | ~['\r\n\\] )* SQuote ; 131 | fragment DQuoteLiteral : DQuote ( EscSeq | ~["\r\n\\] )* DQuote ; 132 | fragment USQuoteLiteral : SQuote ( EscSeq | ~['\r\n\\] )* ; 133 | 134 | fragment DecimalFloatingPointLiteral 135 | : DecDigits DOT DecDigits? ExponentPart? FloatTypeSuffix? 136 | | DOT DecDigits ExponentPart? FloatTypeSuffix? 137 | | DecDigits ExponentPart FloatTypeSuffix? 138 | | DecDigits FloatTypeSuffix 139 | ; 140 | 141 | fragment ExponentPart 142 | : [eE] [+-]? DecDigits 143 | ; 144 | 145 | fragment FloatTypeSuffix 146 | : [fFdD] 147 | ; 148 | 149 | fragment HexadecimalFloatingPointLiteral 150 | : HexSignificand BinaryExponent FloatTypeSuffix? 151 | ; 152 | 153 | fragment HexSignificand 154 | : HexNumeral DOT? 155 | | '0' [xX] HexDigits? DOT HexDigits 156 | ; 157 | 158 | fragment BinaryExponent 159 | : [pP] [+-]? DecDigits 160 | ; 161 | 162 | 163 | // ----------------------------------- 164 | // Character ranges 165 | 166 | fragment NameChar 167 | : NameStartChar 168 | | '0'..'9' 169 | | Underscore 170 | | '\u00B7' 171 | | '\u0300'..'\u036F' 172 | | '\u203F'..'\u2040' 173 | ; 174 | 175 | fragment NameStartChar 176 | : 'A'..'Z' 177 | | 'a'..'z' 178 | | '\u00C0'..'\u00D6' 179 | | '\u00D8'..'\u00F6' 180 | | '\u00F8'..'\u02FF' 181 | | '\u0370'..'\u037D' 182 | | '\u037F'..'\u1FFF' 183 | | '\u200C'..'\u200D' 184 | | '\u2070'..'\u218F' 185 | | '\u2C00'..'\u2FEF' 186 | | '\u3001'..'\uD7FF' 187 | | '\uF900'..'\uFDCF' 188 | | '\uFDF0'..'\uFFFD' 189 | ; // ignores | ['\u10000-'\uEFFFF] ; 190 | 191 | 192 | fragment JavaLetter 193 | : [a-zA-Z$_] // "java letters" below 0xFF 194 | | JavaUnicodeChars 195 | ; 196 | 197 | fragment JavaLetterOrDigit 198 | : [a-zA-Z0-9$_] // "java letters or digits" below 0xFF 199 | | JavaUnicodeChars 200 | ; 201 | 202 | // covers all characters above 0xFF which are not a surrogate 203 | // and UTF-16 surrogate pairs encodings for U+10000 to U+10FFFF 204 | fragment JavaUnicodeChars 205 | : ~[\u0000-\u00FF\uD800-\uDBFF] {Character.isJavaIdentifierPart(_input.LA(-1))}? 206 | | [\uD800-\uDBFF] [\uDC00-\uDFFF] {Character.isJavaIdentifierPart(Character.toCodePoint((char)_input.LA(-2), (char)_input.LA(-1)))}? 207 | ; 208 | 209 | 210 | // ----------------------------------- 211 | // Types 212 | 213 | fragment Boolean : 'boolean' ; 214 | fragment Byte : 'byte' ; 215 | fragment Short : 'short' ; 216 | fragment Int : 'int' ; 217 | fragment Long : 'long' ; 218 | fragment Char : 'char' ; 219 | fragment Float : 'float' ; 220 | fragment Double : 'double' ; 221 | 222 | fragment True : 'true' ; 223 | fragment False : 'false' ; 224 | 225 | 226 | // ----------------------------------- 227 | // Symbols 228 | 229 | fragment Esc : '\\' ; 230 | fragment Colon : ':' ; 231 | fragment DColon : '::' ; 232 | fragment SQuote : '\'' ; 233 | fragment DQuote : '"' ; 234 | fragment BQuote : '`' ; 235 | fragment LParen : '(' ; 236 | fragment RParen : ')' ; 237 | fragment LBrace : '{' ; 238 | fragment RBrace : '}' ; 239 | fragment LBrack : '[' ; 240 | fragment RBrack : ']' ; 241 | fragment RArrow : '->' ; 242 | fragment Lt : '<' ; 243 | fragment Gt : '>' ; 244 | fragment Lte : '<=' ; 245 | fragment Gte : '>=' ; 246 | fragment Equal : '=' ; 247 | fragment NotEqual : '!=' ; 248 | fragment Question : '?' ; 249 | fragment Bang : '!' ; 250 | fragment Star : '*' ; 251 | fragment Slash : '/' ; 252 | fragment Percent : '%' ; 253 | fragment Caret : '^' ; 254 | fragment Plus : '+' ; 255 | fragment Minus : '-' ; 256 | fragment PlusAssign : '+=' ; 257 | fragment MinusAssign : '-=' ; 258 | fragment MulAssign : '*=' ; 259 | fragment DivAssign : '/=' ; 260 | fragment AndAssign : '&=' ; 261 | fragment OrAssign : '|=' ; 262 | fragment XOrAssign : '^=' ; 263 | fragment ModAssign : '%=' ; 264 | fragment LShiftAssign : '<<=' ; 265 | fragment RShiftAssign : '>>=' ; 266 | fragment URShiftAssign : '>>>='; 267 | fragment Underscore : '_' ; 268 | fragment Pipe : '|' ; 269 | fragment Amp : '&' ; 270 | fragment And : '&&' ; 271 | fragment Or : '||' ; 272 | fragment Inc : '++' ; 273 | fragment Dec : '--' ; 274 | fragment LShift : '<<' ; 275 | fragment RShift : '>>' ; 276 | fragment Dollar : '$' ; 277 | fragment Comma : ',' ; 278 | fragment Semi : ';' ; 279 | fragment Dot : '.' ; 280 | fragment Range : '..' ; 281 | fragment Ellipsis : '...' ; 282 | fragment At : '@' ; 283 | fragment Pound : '#' ; 284 | fragment Tilde : '~' ; 285 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit 88 | 89 | # Use the maximum available, or set MAX_FD != -1 to use that value. 90 | MAX_FD=maximum 91 | 92 | warn () { 93 | echo "$*" 94 | } >&2 95 | 96 | die () { 97 | echo 98 | echo "$*" 99 | echo 100 | exit 1 101 | } >&2 102 | 103 | # OS specific support (must be 'true' or 'false'). 104 | cygwin=false 105 | msys=false 106 | darwin=false 107 | nonstop=false 108 | case "$( uname )" in #( 109 | CYGWIN* ) cygwin=true ;; #( 110 | Darwin* ) darwin=true ;; #( 111 | MSYS* | MINGW* ) msys=true ;; #( 112 | NONSTOP* ) nonstop=true ;; 113 | esac 114 | 115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 116 | 117 | 118 | # Determine the Java command to use to start the JVM. 119 | if [ -n "$JAVA_HOME" ] ; then 120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 121 | # IBM's JDK on AIX uses strange locations for the executables 122 | JAVACMD=$JAVA_HOME/jre/sh/java 123 | else 124 | JAVACMD=$JAVA_HOME/bin/java 125 | fi 126 | if [ ! -x "$JAVACMD" ] ; then 127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 128 | 129 | Please set the JAVA_HOME variable in your environment to match the 130 | location of your Java installation." 131 | fi 132 | else 133 | JAVACMD=java 134 | if ! command -v java >/dev/null 2>&1 135 | then 136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | fi 142 | 143 | # Increase the maximum file descriptors if we can. 144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 145 | case $MAX_FD in #( 146 | max*) 147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 148 | # shellcheck disable=SC3045 149 | MAX_FD=$( ulimit -H -n ) || 150 | warn "Could not query maximum file descriptor limit" 151 | esac 152 | case $MAX_FD in #( 153 | '' | soft) :;; #( 154 | *) 155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 156 | # shellcheck disable=SC3045 157 | ulimit -n "$MAX_FD" || 158 | warn "Could not set maximum file descriptor limit to $MAX_FD" 159 | esac 160 | fi 161 | 162 | # Collect all arguments for the java command, stacking in reverse order: 163 | # * args from the command line 164 | # * the main class name 165 | # * -classpath 166 | # * -D...appname settings 167 | # * --module-path (only if needed) 168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 169 | 170 | # For Cygwin or MSYS, switch paths to Windows format before running java 171 | if "$cygwin" || "$msys" ; then 172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 174 | 175 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 176 | 177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 178 | for arg do 179 | if 180 | case $arg in #( 181 | -*) false ;; # don't mess with options #( 182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 183 | [ -e "$t" ] ;; #( 184 | *) false ;; 185 | esac 186 | then 187 | arg=$( cygpath --path --ignore --mixed "$arg" ) 188 | fi 189 | # Roll the args list around exactly as many times as the number of 190 | # args, so each arg winds up back in the position where it started, but 191 | # possibly modified. 192 | # 193 | # NB: a `for` loop captures its iteration list before it begins, so 194 | # changing the positional parameters here affects neither the number of 195 | # iterations, nor the values presented in `arg`. 196 | shift # remove old arg 197 | set -- "$@" "$arg" # push replacement arg 198 | done 199 | fi 200 | 201 | 202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 204 | 205 | # Collect all arguments for the java command; 206 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 207 | # shell script including quotes and variable substitutions, so put them in 208 | # double quotes to make sure that they get re-expanded; and 209 | # * put everything else in single quotes, so that it's not re-expanded. 210 | 211 | set -- \ 212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 213 | -classpath "$CLASSPATH" \ 214 | org.gradle.wrapper.GradleWrapperMain \ 215 | "$@" 216 | 217 | # Stop when "xargs" is not available. 218 | if ! command -v xargs >/dev/null 2>&1 219 | then 220 | die "xargs is not available" 221 | fi 222 | 223 | # Use "xargs" to parse quoted args. 224 | # 225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 226 | # 227 | # In Bash we could simply go: 228 | # 229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 230 | # set -- "${ARGS[@]}" "$@" 231 | # 232 | # but POSIX shell has neither arrays nor command substitution, so instead we 233 | # post-process each arg (as a line of input to sed) to backslash-escape any 234 | # character that might be a shell metacharacter, then use eval to reverse 235 | # that process (while maintaining the separation between arguments), and wrap 236 | # the whole thing up as a single "set" statement. 237 | # 238 | # This will of course break if any of these variables contains a newline or 239 | # an unmatched quote. 240 | # 241 | 242 | eval "set -- $( 243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 244 | xargs -n1 | 245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 246 | tr '\n' ' ' 247 | )" '"$@"' 248 | 249 | exec "$JAVACMD" "$@" 250 | -------------------------------------------------------------------------------- /src/main/antlr/LexUnicode.g4: -------------------------------------------------------------------------------- 1 | /* 2 | * [The "BSD license"] 3 | * Copyright (c) 2014-2015 Gerald Rosenberg 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. The name of the author may not be used to endorse or promote products 16 | * derived from this software without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /** 31 | * A generally reusable Unicode character set description for import in to Lexer grammars. 32 | * 33 | * Modified 2015.06.16 gbr - 34 | * -- generalized for inclusion into the ANTLRv4 grammar distribution 35 | * 36 | */ 37 | 38 | lexer grammar LexUnicode; 39 | 40 | // ====================================================== 41 | // Lexer fragments 42 | // 43 | 44 | fragment UnicodeLetter 45 | : UnicodeClass_LU 46 | | UnicodeClass_LL 47 | | UnicodeClass_LT 48 | | UnicodeClass_LM 49 | | UnicodeClass_LO 50 | ; 51 | 52 | fragment UnicodeClass_LU 53 | : '\u0041'..'\u005a' 54 | | '\u00c0'..'\u00d6' 55 | | '\u00d8'..'\u00de' 56 | | '\u0100'..'\u0136' 57 | | '\u0139'..'\u0147' 58 | | '\u014a'..'\u0178' 59 | | '\u0179'..'\u017d' 60 | | '\u0181'..'\u0182' 61 | | '\u0184'..'\u0186' 62 | | '\u0187'..'\u0189' 63 | | '\u018a'..'\u018b' 64 | | '\u018e'..'\u0191' 65 | | '\u0193'..'\u0194' 66 | | '\u0196'..'\u0198' 67 | | '\u019c'..'\u019d' 68 | | '\u019f'..'\u01a0' 69 | | '\u01a2'..'\u01a6' 70 | | '\u01a7'..'\u01a9' 71 | | '\u01ac'..'\u01ae' 72 | | '\u01af'..'\u01b1' 73 | | '\u01b2'..'\u01b3' 74 | | '\u01b5'..'\u01b7' 75 | | '\u01b8'..'\u01bc' 76 | | '\u01c4'..'\u01cd' 77 | | '\u01cf'..'\u01db' 78 | | '\u01de'..'\u01ee' 79 | | '\u01f1'..'\u01f4' 80 | | '\u01f6'..'\u01f8' 81 | | '\u01fa'..'\u0232' 82 | | '\u023a'..'\u023b' 83 | | '\u023d'..'\u023e' 84 | | '\u0241'..'\u0243' 85 | | '\u0244'..'\u0246' 86 | | '\u0248'..'\u024e' 87 | | '\u0370'..'\u0372' 88 | | '\u0376'..'\u037f' 89 | | '\u0386'..'\u0388' 90 | | '\u0389'..'\u038a' 91 | | '\u038c'..'\u038e' 92 | | '\u038f'..'\u0391' 93 | | '\u0392'..'\u03a1' 94 | | '\u03a3'..'\u03ab' 95 | | '\u03cf'..'\u03d2' 96 | | '\u03d3'..'\u03d4' 97 | | '\u03d8'..'\u03ee' 98 | | '\u03f4'..'\u03f7' 99 | | '\u03f9'..'\u03fa' 100 | | '\u03fd'..'\u042f' 101 | | '\u0460'..'\u0480' 102 | | '\u048a'..'\u04c0' 103 | | '\u04c1'..'\u04cd' 104 | | '\u04d0'..'\u052e' 105 | | '\u0531'..'\u0556' 106 | | '\u10a0'..'\u10c5' 107 | | '\u10c7'..'\u10cd' 108 | | '\u1e00'..'\u1e94' 109 | | '\u1e9e'..'\u1efe' 110 | | '\u1f08'..'\u1f0f' 111 | | '\u1f18'..'\u1f1d' 112 | | '\u1f28'..'\u1f2f' 113 | | '\u1f38'..'\u1f3f' 114 | | '\u1f48'..'\u1f4d' 115 | | '\u1f59'..'\u1f5f' 116 | | '\u1f68'..'\u1f6f' 117 | | '\u1fb8'..'\u1fbb' 118 | | '\u1fc8'..'\u1fcb' 119 | | '\u1fd8'..'\u1fdb' 120 | | '\u1fe8'..'\u1fec' 121 | | '\u1ff8'..'\u1ffb' 122 | | '\u2102'..'\u2107' 123 | | '\u210b'..'\u210d' 124 | | '\u2110'..'\u2112' 125 | | '\u2115'..'\u2119' 126 | | '\u211a'..'\u211d' 127 | | '\u2124'..'\u212a' 128 | | '\u212b'..'\u212d' 129 | | '\u2130'..'\u2133' 130 | | '\u213e'..'\u213f' 131 | | '\u2145'..'\u2183' 132 | | '\u2c00'..'\u2c2e' 133 | | '\u2c60'..'\u2c62' 134 | | '\u2c63'..'\u2c64' 135 | | '\u2c67'..'\u2c6d' 136 | | '\u2c6e'..'\u2c70' 137 | | '\u2c72'..'\u2c75' 138 | | '\u2c7e'..'\u2c80' 139 | | '\u2c82'..'\u2ce2' 140 | | '\u2ceb'..'\u2ced' 141 | | '\u2cf2'..'\ua640' 142 | | '\ua642'..'\ua66c' 143 | | '\ua680'..'\ua69a' 144 | | '\ua722'..'\ua72e' 145 | | '\ua732'..'\ua76e' 146 | | '\ua779'..'\ua77d' 147 | | '\ua77e'..'\ua786' 148 | | '\ua78b'..'\ua78d' 149 | | '\ua790'..'\ua792' 150 | | '\ua796'..'\ua7aa' 151 | | '\ua7ab'..'\ua7ad' 152 | | '\ua7b0'..'\ua7b1' 153 | | '\uff21'..'\uff3a' 154 | ; 155 | 156 | fragment UnicodeClass_LL 157 | : '\u0061'..'\u007A' 158 | | '\u00b5'..'\u00df' 159 | | '\u00e0'..'\u00f6' 160 | | '\u00f8'..'\u00ff' 161 | | '\u0101'..'\u0137' 162 | | '\u0138'..'\u0148' 163 | | '\u0149'..'\u0177' 164 | | '\u017a'..'\u017e' 165 | | '\u017f'..'\u0180' 166 | | '\u0183'..'\u0185' 167 | | '\u0188'..'\u018c' 168 | | '\u018d'..'\u0192' 169 | | '\u0195'..'\u0199' 170 | | '\u019a'..'\u019b' 171 | | '\u019e'..'\u01a1' 172 | | '\u01a3'..'\u01a5' 173 | | '\u01a8'..'\u01aa' 174 | | '\u01ab'..'\u01ad' 175 | | '\u01b0'..'\u01b4' 176 | | '\u01b6'..'\u01b9' 177 | | '\u01ba'..'\u01bd' 178 | | '\u01be'..'\u01bf' 179 | | '\u01c6'..'\u01cc' 180 | | '\u01ce'..'\u01dc' 181 | | '\u01dd'..'\u01ef' 182 | | '\u01f0'..'\u01f3' 183 | | '\u01f5'..'\u01f9' 184 | | '\u01fb'..'\u0233' 185 | | '\u0234'..'\u0239' 186 | | '\u023c'..'\u023f' 187 | | '\u0240'..'\u0242' 188 | | '\u0247'..'\u024f' 189 | | '\u0250'..'\u0293' 190 | | '\u0295'..'\u02af' 191 | | '\u0371'..'\u0373' 192 | | '\u0377'..'\u037b' 193 | | '\u037c'..'\u037d' 194 | | '\u0390'..'\u03ac' 195 | | '\u03ad'..'\u03ce' 196 | | '\u03d0'..'\u03d1' 197 | | '\u03d5'..'\u03d7' 198 | | '\u03d9'..'\u03ef' 199 | | '\u03f0'..'\u03f3' 200 | | '\u03f5'..'\u03fb' 201 | | '\u03fc'..'\u0430' 202 | | '\u0431'..'\u045f' 203 | | '\u0461'..'\u0481' 204 | | '\u048b'..'\u04bf' 205 | | '\u04c2'..'\u04ce' 206 | | '\u04cf'..'\u052f' 207 | | '\u0561'..'\u0587' 208 | | '\u1d00'..'\u1d2b' 209 | | '\u1d6b'..'\u1d77' 210 | | '\u1d79'..'\u1d9a' 211 | | '\u1e01'..'\u1e95' 212 | | '\u1e96'..'\u1e9d' 213 | | '\u1e9f'..'\u1eff' 214 | | '\u1f00'..'\u1f07' 215 | | '\u1f10'..'\u1f15' 216 | | '\u1f20'..'\u1f27' 217 | | '\u1f30'..'\u1f37' 218 | | '\u1f40'..'\u1f45' 219 | | '\u1f50'..'\u1f57' 220 | | '\u1f60'..'\u1f67' 221 | | '\u1f70'..'\u1f7d' 222 | | '\u1f80'..'\u1f87' 223 | | '\u1f90'..'\u1f97' 224 | | '\u1fa0'..'\u1fa7' 225 | | '\u1fb0'..'\u1fb4' 226 | | '\u1fb6'..'\u1fb7' 227 | | '\u1fbe'..'\u1fc2' 228 | | '\u1fc3'..'\u1fc4' 229 | | '\u1fc6'..'\u1fc7' 230 | | '\u1fd0'..'\u1fd3' 231 | | '\u1fd6'..'\u1fd7' 232 | | '\u1fe0'..'\u1fe7' 233 | | '\u1ff2'..'\u1ff4' 234 | | '\u1ff6'..'\u1ff7' 235 | | '\u210a'..'\u210e' 236 | | '\u210f'..'\u2113' 237 | | '\u212f'..'\u2139' 238 | | '\u213c'..'\u213d' 239 | | '\u2146'..'\u2149' 240 | | '\u214e'..'\u2184' 241 | | '\u2c30'..'\u2c5e' 242 | | '\u2c61'..'\u2c65' 243 | | '\u2c66'..'\u2c6c' 244 | | '\u2c71'..'\u2c73' 245 | | '\u2c74'..'\u2c76' 246 | | '\u2c77'..'\u2c7b' 247 | | '\u2c81'..'\u2ce3' 248 | | '\u2ce4'..'\u2cec' 249 | | '\u2cee'..'\u2cf3' 250 | | '\u2d00'..'\u2d25' 251 | | '\u2d27'..'\u2d2d' 252 | | '\ua641'..'\ua66d' 253 | | '\ua681'..'\ua69b' 254 | | '\ua723'..'\ua72f' 255 | | '\ua730'..'\ua731' 256 | | '\ua733'..'\ua771' 257 | | '\ua772'..'\ua778' 258 | | '\ua77a'..'\ua77c' 259 | | '\ua77f'..'\ua787' 260 | | '\ua78c'..'\ua78e' 261 | | '\ua791'..'\ua793' 262 | | '\ua794'..'\ua795' 263 | | '\ua797'..'\ua7a9' 264 | | '\ua7fa'..'\uab30' 265 | | '\uab31'..'\uab5a' 266 | | '\uab64'..'\uab65' 267 | | '\ufb00'..'\ufb06' 268 | | '\ufb13'..'\ufb17' 269 | | '\uff41'..'\uff5a' 270 | ; 271 | 272 | fragment UnicodeClass_LT 273 | : '\u01c5'..'\u01cb' 274 | | '\u01f2'..'\u1f88' 275 | | '\u1f89'..'\u1f8f' 276 | | '\u1f98'..'\u1f9f' 277 | | '\u1fa8'..'\u1faf' 278 | | '\u1fbc'..'\u1fcc' 279 | | '\u1ffc'..'\u1ffc' 280 | ; 281 | 282 | fragment UnicodeClass_LM 283 | : '\u02b0'..'\u02c1' 284 | | '\u02c6'..'\u02d1' 285 | | '\u02e0'..'\u02e4' 286 | | '\u02ec'..'\u02ee' 287 | | '\u0374'..'\u037a' 288 | | '\u0559'..'\u0640' 289 | | '\u06e5'..'\u06e6' 290 | | '\u07f4'..'\u07f5' 291 | | '\u07fa'..'\u081a' 292 | | '\u0824'..'\u0828' 293 | | '\u0971'..'\u0e46' 294 | | '\u0ec6'..'\u10fc' 295 | | '\u17d7'..'\u1843' 296 | | '\u1aa7'..'\u1c78' 297 | | '\u1c79'..'\u1c7d' 298 | | '\u1d2c'..'\u1d6a' 299 | | '\u1d78'..'\u1d9b' 300 | | '\u1d9c'..'\u1dbf' 301 | | '\u2071'..'\u207f' 302 | | '\u2090'..'\u209c' 303 | | '\u2c7c'..'\u2c7d' 304 | | '\u2d6f'..'\u2e2f' 305 | | '\u3005'..'\u3031' 306 | | '\u3032'..'\u3035' 307 | | '\u303b'..'\u309d' 308 | | '\u309e'..'\u30fc' 309 | | '\u30fd'..'\u30fe' 310 | | '\ua015'..'\ua4f8' 311 | | '\ua4f9'..'\ua4fd' 312 | | '\ua60c'..'\ua67f' 313 | | '\ua69c'..'\ua69d' 314 | | '\ua717'..'\ua71f' 315 | | '\ua770'..'\ua788' 316 | | '\ua7f8'..'\ua7f9' 317 | | '\ua9cf'..'\ua9e6' 318 | | '\uaa70'..'\uaadd' 319 | | '\uaaf3'..'\uaaf4' 320 | | '\uab5c'..'\uab5f' 321 | | '\uff70'..'\uff9e' 322 | | '\uff9f'..'\uff9f' 323 | ; 324 | 325 | fragment UnicodeClass_LO 326 | : '\u00aa'..'\u00ba' 327 | | '\u01bb'..'\u01c0' 328 | | '\u01c1'..'\u01c3' 329 | | '\u0294'..'\u05d0' 330 | | '\u05d1'..'\u05ea' 331 | | '\u05f0'..'\u05f2' 332 | | '\u0620'..'\u063f' 333 | | '\u0641'..'\u064a' 334 | | '\u066e'..'\u066f' 335 | | '\u0671'..'\u06d3' 336 | | '\u06d5'..'\u06ee' 337 | | '\u06ef'..'\u06fa' 338 | | '\u06fb'..'\u06fc' 339 | | '\u06ff'..'\u0710' 340 | | '\u0712'..'\u072f' 341 | | '\u074d'..'\u07a5' 342 | | '\u07b1'..'\u07ca' 343 | | '\u07cb'..'\u07ea' 344 | | '\u0800'..'\u0815' 345 | | '\u0840'..'\u0858' 346 | | '\u08a0'..'\u08b2' 347 | | '\u0904'..'\u0939' 348 | | '\u093d'..'\u0950' 349 | | '\u0958'..'\u0961' 350 | | '\u0972'..'\u0980' 351 | | '\u0985'..'\u098c' 352 | | '\u098f'..'\u0990' 353 | | '\u0993'..'\u09a8' 354 | | '\u09aa'..'\u09b0' 355 | | '\u09b2'..'\u09b6' 356 | | '\u09b7'..'\u09b9' 357 | | '\u09bd'..'\u09ce' 358 | | '\u09dc'..'\u09dd' 359 | | '\u09df'..'\u09e1' 360 | | '\u09f0'..'\u09f1' 361 | | '\u0a05'..'\u0a0a' 362 | | '\u0a0f'..'\u0a10' 363 | | '\u0a13'..'\u0a28' 364 | | '\u0a2a'..'\u0a30' 365 | | '\u0a32'..'\u0a33' 366 | | '\u0a35'..'\u0a36' 367 | | '\u0a38'..'\u0a39' 368 | | '\u0a59'..'\u0a5c' 369 | | '\u0a5e'..'\u0a72' 370 | | '\u0a73'..'\u0a74' 371 | | '\u0a85'..'\u0a8d' 372 | | '\u0a8f'..'\u0a91' 373 | | '\u0a93'..'\u0aa8' 374 | | '\u0aaa'..'\u0ab0' 375 | | '\u0ab2'..'\u0ab3' 376 | | '\u0ab5'..'\u0ab9' 377 | | '\u0abd'..'\u0ad0' 378 | | '\u0ae0'..'\u0ae1' 379 | | '\u0b05'..'\u0b0c' 380 | | '\u0b0f'..'\u0b10' 381 | | '\u0b13'..'\u0b28' 382 | | '\u0b2a'..'\u0b30' 383 | | '\u0b32'..'\u0b33' 384 | | '\u0b35'..'\u0b39' 385 | | '\u0b3d'..'\u0b5c' 386 | | '\u0b5d'..'\u0b5f' 387 | | '\u0b60'..'\u0b61' 388 | | '\u0b71'..'\u0b83' 389 | | '\u0b85'..'\u0b8a' 390 | | '\u0b8e'..'\u0b90' 391 | | '\u0b92'..'\u0b95' 392 | | '\u0b99'..'\u0b9a' 393 | | '\u0b9c'..'\u0b9e' 394 | | '\u0b9f'..'\u0ba3' 395 | | '\u0ba4'..'\u0ba8' 396 | | '\u0ba9'..'\u0baa' 397 | | '\u0bae'..'\u0bb9' 398 | | '\u0bd0'..'\u0c05' 399 | | '\u0c06'..'\u0c0c' 400 | | '\u0c0e'..'\u0c10' 401 | | '\u0c12'..'\u0c28' 402 | | '\u0c2a'..'\u0c39' 403 | | '\u0c3d'..'\u0c58' 404 | | '\u0c59'..'\u0c60' 405 | | '\u0c61'..'\u0c85' 406 | | '\u0c86'..'\u0c8c' 407 | | '\u0c8e'..'\u0c90' 408 | | '\u0c92'..'\u0ca8' 409 | | '\u0caa'..'\u0cb3' 410 | | '\u0cb5'..'\u0cb9' 411 | | '\u0cbd'..'\u0cde' 412 | | '\u0ce0'..'\u0ce1' 413 | | '\u0cf1'..'\u0cf2' 414 | | '\u0d05'..'\u0d0c' 415 | | '\u0d0e'..'\u0d10' 416 | | '\u0d12'..'\u0d3a' 417 | | '\u0d3d'..'\u0d4e' 418 | | '\u0d60'..'\u0d61' 419 | | '\u0d7a'..'\u0d7f' 420 | | '\u0d85'..'\u0d96' 421 | | '\u0d9a'..'\u0db1' 422 | | '\u0db3'..'\u0dbb' 423 | | '\u0dbd'..'\u0dc0' 424 | | '\u0dc1'..'\u0dc6' 425 | | '\u0e01'..'\u0e30' 426 | | '\u0e32'..'\u0e33' 427 | | '\u0e40'..'\u0e45' 428 | | '\u0e81'..'\u0e82' 429 | | '\u0e84'..'\u0e87' 430 | | '\u0e88'..'\u0e8a' 431 | | '\u0e8d'..'\u0e94' 432 | | '\u0e95'..'\u0e97' 433 | | '\u0e99'..'\u0e9f' 434 | | '\u0ea1'..'\u0ea3' 435 | | '\u0ea5'..'\u0ea7' 436 | | '\u0eaa'..'\u0eab' 437 | | '\u0ead'..'\u0eb0' 438 | | '\u0eb2'..'\u0eb3' 439 | | '\u0ebd'..'\u0ec0' 440 | | '\u0ec1'..'\u0ec4' 441 | | '\u0edc'..'\u0edf' 442 | | '\u0f00'..'\u0f40' 443 | | '\u0f41'..'\u0f47' 444 | | '\u0f49'..'\u0f6c' 445 | | '\u0f88'..'\u0f8c' 446 | | '\u1000'..'\u102a' 447 | | '\u103f'..'\u1050' 448 | | '\u1051'..'\u1055' 449 | | '\u105a'..'\u105d' 450 | | '\u1061'..'\u1065' 451 | | '\u1066'..'\u106e' 452 | | '\u106f'..'\u1070' 453 | | '\u1075'..'\u1081' 454 | | '\u108e'..'\u10d0' 455 | | '\u10d1'..'\u10fa' 456 | | '\u10fd'..'\u1248' 457 | | '\u124a'..'\u124d' 458 | | '\u1250'..'\u1256' 459 | | '\u1258'..'\u125a' 460 | | '\u125b'..'\u125d' 461 | | '\u1260'..'\u1288' 462 | | '\u128a'..'\u128d' 463 | | '\u1290'..'\u12b0' 464 | | '\u12b2'..'\u12b5' 465 | | '\u12b8'..'\u12be' 466 | | '\u12c0'..'\u12c2' 467 | | '\u12c3'..'\u12c5' 468 | | '\u12c8'..'\u12d6' 469 | | '\u12d8'..'\u1310' 470 | | '\u1312'..'\u1315' 471 | | '\u1318'..'\u135a' 472 | | '\u1380'..'\u138f' 473 | | '\u13a0'..'\u13f4' 474 | | '\u1401'..'\u166c' 475 | | '\u166f'..'\u167f' 476 | | '\u1681'..'\u169a' 477 | | '\u16a0'..'\u16ea' 478 | | '\u16f1'..'\u16f8' 479 | | '\u1700'..'\u170c' 480 | | '\u170e'..'\u1711' 481 | | '\u1720'..'\u1731' 482 | | '\u1740'..'\u1751' 483 | | '\u1760'..'\u176c' 484 | | '\u176e'..'\u1770' 485 | | '\u1780'..'\u17b3' 486 | | '\u17dc'..'\u1820' 487 | | '\u1821'..'\u1842' 488 | | '\u1844'..'\u1877' 489 | | '\u1880'..'\u18a8' 490 | | '\u18aa'..'\u18b0' 491 | | '\u18b1'..'\u18f5' 492 | | '\u1900'..'\u191e' 493 | | '\u1950'..'\u196d' 494 | | '\u1970'..'\u1974' 495 | | '\u1980'..'\u19ab' 496 | | '\u19c1'..'\u19c7' 497 | | '\u1a00'..'\u1a16' 498 | | '\u1a20'..'\u1a54' 499 | | '\u1b05'..'\u1b33' 500 | | '\u1b45'..'\u1b4b' 501 | | '\u1b83'..'\u1ba0' 502 | | '\u1bae'..'\u1baf' 503 | | '\u1bba'..'\u1be5' 504 | | '\u1c00'..'\u1c23' 505 | | '\u1c4d'..'\u1c4f' 506 | | '\u1c5a'..'\u1c77' 507 | | '\u1ce9'..'\u1cec' 508 | | '\u1cee'..'\u1cf1' 509 | | '\u1cf5'..'\u1cf6' 510 | | '\u2135'..'\u2138' 511 | | '\u2d30'..'\u2d67' 512 | | '\u2d80'..'\u2d96' 513 | | '\u2da0'..'\u2da6' 514 | | '\u2da8'..'\u2dae' 515 | | '\u2db0'..'\u2db6' 516 | | '\u2db8'..'\u2dbe' 517 | | '\u2dc0'..'\u2dc6' 518 | | '\u2dc8'..'\u2dce' 519 | | '\u2dd0'..'\u2dd6' 520 | | '\u2dd8'..'\u2dde' 521 | | '\u3006'..'\u303c' 522 | | '\u3041'..'\u3096' 523 | | '\u309f'..'\u30a1' 524 | | '\u30a2'..'\u30fa' 525 | | '\u30ff'..'\u3105' 526 | | '\u3106'..'\u312d' 527 | | '\u3131'..'\u318e' 528 | | '\u31a0'..'\u31ba' 529 | | '\u31f0'..'\u31ff' 530 | | '\u3400'..'\u4db5' 531 | | '\u4e00'..'\u9fcc' 532 | | '\ua000'..'\ua014' 533 | | '\ua016'..'\ua48c' 534 | | '\ua4d0'..'\ua4f7' 535 | | '\ua500'..'\ua60b' 536 | | '\ua610'..'\ua61f' 537 | | '\ua62a'..'\ua62b' 538 | | '\ua66e'..'\ua6a0' 539 | | '\ua6a1'..'\ua6e5' 540 | | '\ua7f7'..'\ua7fb' 541 | | '\ua7fc'..'\ua801' 542 | | '\ua803'..'\ua805' 543 | | '\ua807'..'\ua80a' 544 | | '\ua80c'..'\ua822' 545 | | '\ua840'..'\ua873' 546 | | '\ua882'..'\ua8b3' 547 | | '\ua8f2'..'\ua8f7' 548 | | '\ua8fb'..'\ua90a' 549 | | '\ua90b'..'\ua925' 550 | | '\ua930'..'\ua946' 551 | | '\ua960'..'\ua97c' 552 | | '\ua984'..'\ua9b2' 553 | | '\ua9e0'..'\ua9e4' 554 | | '\ua9e7'..'\ua9ef' 555 | | '\ua9fa'..'\ua9fe' 556 | | '\uaa00'..'\uaa28' 557 | | '\uaa40'..'\uaa42' 558 | | '\uaa44'..'\uaa4b' 559 | | '\uaa60'..'\uaa6f' 560 | | '\uaa71'..'\uaa76' 561 | | '\uaa7a'..'\uaa7e' 562 | | '\uaa7f'..'\uaaaf' 563 | | '\uaab1'..'\uaab5' 564 | | '\uaab6'..'\uaab9' 565 | | '\uaaba'..'\uaabd' 566 | | '\uaac0'..'\uaac2' 567 | | '\uaadb'..'\uaadc' 568 | | '\uaae0'..'\uaaea' 569 | | '\uaaf2'..'\uab01' 570 | | '\uab02'..'\uab06' 571 | | '\uab09'..'\uab0e' 572 | | '\uab11'..'\uab16' 573 | | '\uab20'..'\uab26' 574 | | '\uab28'..'\uab2e' 575 | | '\uabc0'..'\uabe2' 576 | | '\uac00'..'\ud7a3' 577 | | '\ud7b0'..'\ud7c6' 578 | | '\ud7cb'..'\ud7fb' 579 | | '\uf900'..'\ufa6d' 580 | | '\ufa70'..'\ufad9' 581 | | '\ufb1d'..'\ufb1f' 582 | | '\ufb20'..'\ufb28' 583 | | '\ufb2a'..'\ufb36' 584 | | '\ufb38'..'\ufb3c' 585 | | '\ufb3e'..'\ufb40' 586 | | '\ufb41'..'\ufb43' 587 | | '\ufb44'..'\ufb46' 588 | | '\ufb47'..'\ufbb1' 589 | | '\ufbd3'..'\ufd3d' 590 | | '\ufd50'..'\ufd8f' 591 | | '\ufd92'..'\ufdc7' 592 | | '\ufdf0'..'\ufdfb' 593 | | '\ufe70'..'\ufe74' 594 | | '\ufe76'..'\ufefc' 595 | | '\uff66'..'\uff6f' 596 | | '\uff71'..'\uff9d' 597 | | '\uffa0'..'\uffbe' 598 | | '\uffc2'..'\uffc7' 599 | | '\uffca'..'\uffcf' 600 | | '\uffd2'..'\uffd7' 601 | | '\uffda'..'\uffdc' 602 | ; 603 | 604 | fragment UnicodeDigit // UnicodeClass_ND 605 | : '\u0030'..'\u0039' 606 | | '\u0660'..'\u0669' 607 | | '\u06f0'..'\u06f9' 608 | | '\u07c0'..'\u07c9' 609 | | '\u0966'..'\u096f' 610 | | '\u09e6'..'\u09ef' 611 | | '\u0a66'..'\u0a6f' 612 | | '\u0ae6'..'\u0aef' 613 | | '\u0b66'..'\u0b6f' 614 | | '\u0be6'..'\u0bef' 615 | | '\u0c66'..'\u0c6f' 616 | | '\u0ce6'..'\u0cef' 617 | | '\u0d66'..'\u0d6f' 618 | | '\u0de6'..'\u0def' 619 | | '\u0e50'..'\u0e59' 620 | | '\u0ed0'..'\u0ed9' 621 | | '\u0f20'..'\u0f29' 622 | | '\u1040'..'\u1049' 623 | | '\u1090'..'\u1099' 624 | | '\u17e0'..'\u17e9' 625 | | '\u1810'..'\u1819' 626 | | '\u1946'..'\u194f' 627 | | '\u19d0'..'\u19d9' 628 | | '\u1a80'..'\u1a89' 629 | | '\u1a90'..'\u1a99' 630 | | '\u1b50'..'\u1b59' 631 | | '\u1bb0'..'\u1bb9' 632 | | '\u1c40'..'\u1c49' 633 | | '\u1c50'..'\u1c59' 634 | | '\ua620'..'\ua629' 635 | | '\ua8d0'..'\ua8d9' 636 | | '\ua900'..'\ua909' 637 | | '\ua9d0'..'\ua9d9' 638 | | '\ua9f0'..'\ua9f9' 639 | | '\uaa50'..'\uaa59' 640 | | '\uabf0'..'\uabf9' 641 | | '\uff10'..'\uff19' 642 | ; 643 | --------------------------------------------------------------------------------