@tsomaiatech/moxite 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. package/README.md +61 -0
  2. package/idea-plugin/build.gradle.kts +60 -0
  3. package/idea-plugin/gradle.properties +14 -0
  4. package/idea-plugin/settings.gradle.kts +1 -0
  5. package/idea-plugin/src/main/flex/Moxite.flex +101 -0
  6. package/idea-plugin/src/main/grammar/Moxite.bnf +79 -0
  7. package/idea-plugin/src/main/java/tech/tsomaia/moxite/idea/MoxiteFileType.java +39 -0
  8. package/idea-plugin/src/main/java/tech/tsomaia/moxite/idea/MoxiteIcons.java +9 -0
  9. package/idea-plugin/src/main/java/tech/tsomaia/moxite/idea/MoxiteLanguage.java +11 -0
  10. package/idea-plugin/src/main/java/tech/tsomaia/moxite/idea/MoxiteParserDefinition.java +77 -0
  11. package/idea-plugin/src/main/java/tech/tsomaia/moxite/idea/MoxiteSyntaxHighlighter.java +82 -0
  12. package/idea-plugin/src/main/java/tech/tsomaia/moxite/idea/MoxiteSyntaxHighlighterFactory.java +16 -0
  13. package/idea-plugin/src/main/java/tech/tsomaia/moxite/idea/lexer/MoxiteLexerAdapter.java +9 -0
  14. package/idea-plugin/src/main/java/tech/tsomaia/moxite/idea/parser/MoxiteParserUtil.java +6 -0
  15. package/idea-plugin/src/main/java/tech/tsomaia/moxite/idea/psi/MoxiteElementType.java +12 -0
  16. package/idea-plugin/src/main/java/tech/tsomaia/moxite/idea/psi/MoxiteFile.java +25 -0
  17. package/idea-plugin/src/main/java/tech/tsomaia/moxite/idea/psi/MoxiteTokenType.java +17 -0
  18. package/idea-plugin/src/main/resources/META-INF/plugin.xml +32 -0
  19. package/package.json +36 -0
  20. package/src/__tests__/template-engine.test.ts +437 -0
  21. package/src/template-engine.ts +480 -0
  22. package/src/template-manager.ts +75 -0
package/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # Moxite Template Engine
2
+
3
+ Moxite is an extremely fast, structural, and strictly evaluated template engine designed to be completely **indestructible**.
4
+
5
+ Drawing inspiration from modern control flows like Angular 19, Moxite enforces a separation of logic and presentation through a pristine, character-by-character parsed Abstract Syntax Tree (AST). It completely eliminates the use of fuzzy Regular Expressions for parsing, meaning it is impossible to crash the engine with malformed overlapping templates, escaped quotes, or structural injection attacks.
6
+
7
+ ## Why Moxite?
8
+
9
+ Modern template engines (like Jinja, EJS, and Handlebars) rely heavily on Regular Expressions and have slowly mutated into sprawling, poorly-implemented programming languages that execute inline logic, define JSON objects inline, and succumb to context leaks.
10
+
11
+ Moxite takes a diametrically opposed approach:
12
+ 1. **Zero Regex Parsing**: Every token is mapped through a strict, atomic Lexical State machine and evaluated via a recursive descent Parser.
13
+ 2. **Zero Inline Objects**: The Expression Lexer intentionally forbids array closures `[1, 2]` or object structures `{k: v}` to prevent Turing-complete bloat and force developers to construct their data in the core application logic (e.g. TypeScript/Java), not in the view layer.
14
+ 3. **Implicit Safe Navigation**: The engine automatically protects against `null` or `undefined` property access without throwing exceptions, turning `{{ user.profile.name }}` into an implicit `{{ user?.profile?.name }}`.
15
+ 4. **Absolute Polyglot Isolation**: Moxite's domain syntax (`@` and `{{ }}`) will perfectly isolate itself even when overlapping natively with 10 other template engines or syntaxes in the exact same file (C# Razor, PHP, Bash, JSON-LD, etc.).
16
+
17
+ ## Syntax Outline
18
+
19
+ ### Interpolation & Implicit Chaining
20
+ ```mx
21
+ Hello, {{ user.name }}!
22
+ Implicit Safe Output: {{ user.deep.missing.path.shouldNotCrash }}
23
+ ```
24
+
25
+ ### Conditionals (`@if`)
26
+ ```mx
27
+ @if (user.isAdmin)
28
+ Welcome Administrator.
29
+ @else if (user.isGuest === "yes")
30
+ Welcome Guest.
31
+ @else
32
+ Welcome User.
33
+ @endif
34
+ ```
35
+
36
+ ### Loops (`@for`)
37
+ ```mx
38
+ @for (item of items)
39
+ Item: {{ item.name }}
40
+ @endfor
41
+ ```
42
+
43
+ ### Immutable Block Scoping (`@const`)
44
+ ```mx
45
+ @const greeting = "Hello"
46
+ @const role = user.role
47
+
48
+ {{ greeting }}, you are a {{ role }}
49
+ ```
50
+
51
+ ### Pipes (`|`)
52
+ Pipes allow you to pass values through pure formatting functions registered in the evaluation context.
53
+ ```mx
54
+ {{ user.name | upper }}
55
+ {{ total | currency: "USD" }}
56
+ ```
57
+
58
+ ## Structure
59
+
60
+ - `src/`: Contains the pure TypeScript reference implementation of the strict Lexer, AST Parser, and Evaluator (`template-engine.ts`).
61
+ - `idea-plugin/`: An independent Gradle project containing the IntelliJ IDEA plugin. Utilizing Grammar-Kit and JFlex, it tokenizes `.mx` files with the exact deterministic boundary rules as the TypeScript engine, providing native JetBrains syntax highlighting and completion.
@@ -0,0 +1,60 @@
1
+ plugins {
2
+ id("java")
3
+ id("org.jetbrains.intellij") version "1.17.4"
4
+ id("org.jetbrains.grammarkit") version "2022.3.2.2"
5
+ }
6
+
7
+ group = project.properties["pluginGroup"] as String
8
+ version = project.properties["pluginVersion"] as String
9
+
10
+ repositories {
11
+ mavenCentral()
12
+ }
13
+
14
+ // Configure Gradle IntelliJ Plugin
15
+ // Read more: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html
16
+ intellij {
17
+ pluginName.set(project.properties["pluginName"] as String)
18
+ version.set(project.properties["platformVersion"] as String)
19
+ type.set(project.properties["platformType"] as String)
20
+
21
+ // Plugin Dependencies. Uses `java` plugin.
22
+ plugins.set(listOf("com.intellij.java"))
23
+ }
24
+
25
+ tasks {
26
+ // Set the JVM compatibility versions
27
+ withType<JavaCompile> {
28
+ sourceCompatibility = project.properties["javaVersion"] as String
29
+ targetCompatibility = project.properties["javaVersion"] as String
30
+ }
31
+
32
+ patchPluginXml {
33
+ sinceBuild.set(project.properties["pluginSinceBuild"] as String)
34
+ untilBuild.set(project.properties["pluginUntilBuild"] as String)
35
+ }
36
+
37
+ signPlugin {
38
+ certificateChain.set(System.getenv("CERTIFICATE_CHAIN"))
39
+ privateKey.set(System.getenv("PRIVATE_KEY"))
40
+ password.set(System.getenv("PRIVATE_KEY_PASSWORD"))
41
+ }
42
+
43
+ publishPlugin {
44
+ token.set(System.getenv("PUBLISH_TOKEN"))
45
+ }
46
+ }
47
+
48
+ sourceSets {
49
+ main {
50
+ java.srcDirs("src/main/java", "src/main/gen")
51
+ }
52
+ }
53
+
54
+ // Ensure the generate sources directories exist
55
+ val generateLexer = tasks.named("generateLexer")
56
+ val generateParser = tasks.named("generateParser")
57
+
58
+ tasks.withType<JavaCompile> {
59
+ dependsOn(generateLexer, generateParser)
60
+ }
@@ -0,0 +1,14 @@
1
+ pluginGroup = "tech.tsomaia.moxite"
2
+ pluginName = "Moxite Template Support"
3
+ pluginVersion = "1.0.0"
4
+
5
+ # Supported IDE versions
6
+ pluginSinceBuild = "231"
7
+ pluginUntilBuild = "241.*"
8
+
9
+ # IntelliJ Platform Properties
10
+ platformType = "IC"
11
+ platformVersion = "2023.1.5"
12
+
13
+ # Java language level
14
+ javaVersion = "17"
@@ -0,0 +1 @@
1
+ rootProject.name = "moxite-idea"
@@ -0,0 +1,101 @@
1
+ package tech.tsomaia.moxite.idea.lexer;
2
+
3
+ import com.intellij.lexer.FlexLexer;
4
+ import com.intellij.psi.tree.IElementType;
5
+ import tech.tsomaia.moxite.idea.psi.MoxiteTypes;
6
+ import com.intellij.psi.TokenType;
7
+
8
+ %%
9
+
10
+ %class _MoxiteLexer
11
+ %implements FlexLexer
12
+ %unicode
13
+ %function advance
14
+ %type IElementType
15
+ %eof{ return;
16
+ %eof}
17
+
18
+ // Token definitions
19
+ WHITE_SPACE=[\ \n\r\t]+
20
+ IDENTIFIER=[a-zA-Z_$][a-zA-Z0-9_$]*
21
+ NUMBER=[0-9]+(\.[0-9]+)?
22
+ STRING=\"[^\"]*\"|'[^']*'
23
+
24
+ %state IN_TAG_ARGS
25
+ %state IN_INTERPOLATION
26
+
27
+ %%
28
+
29
+ // When in standard TEXT mode, we look for { { or @
30
+ <YYINITIAL> {
31
+ "{{" { yybegin(IN_INTERPOLATION); return MoxiteTypes.INTERP_START; }
32
+
33
+ "@if" / [^a-zA-Z0-9_$] { yybegin(IN_TAG_ARGS); return MoxiteTypes.TAG_IF; }
34
+ "@else if" / [^a-zA-Z0-9_$] { yybegin(IN_TAG_ARGS); return MoxiteTypes.TAG_ELSE_IF; }
35
+ "@else" / [^a-zA-Z0-9_$] { return MoxiteTypes.TAG_ELSE; }
36
+ "@endif" / [^a-zA-Z0-9_$] { return MoxiteTypes.TAG_ENDIF; }
37
+
38
+ "@for" / [^a-zA-Z0-9_$] { yybegin(IN_TAG_ARGS); return MoxiteTypes.TAG_FOR; }
39
+ "@endfor" / [^a-zA-Z0-9_$] { return MoxiteTypes.TAG_ENDFOR; }
40
+
41
+ "@const" / [^a-zA-Z0-9_$] { yybegin(IN_TAG_ARGS); return MoxiteTypes.TAG_CONST; }
42
+
43
+ // Anything else is just text
44
+ [^\{@]+ { return MoxiteTypes.TEXT; }
45
+ "{" { return MoxiteTypes.TEXT; }
46
+ "@" { return MoxiteTypes.TEXT; }
47
+ }
48
+
49
+ // When inside parentheses of a loop or condition
50
+ <IN_TAG_ARGS> {
51
+ {WHITE_SPACE} { return TokenType.WHITE_SPACE; }
52
+ ")" { yybegin(YYINITIAL); return MoxiteTypes.RPAREN; }
53
+ "(" { return MoxiteTypes.LPAREN; }
54
+ "[" { return MoxiteTypes.LBRACKET; }
55
+ "]" { return MoxiteTypes.RBRACKET; }
56
+ "." { return MoxiteTypes.DOT; }
57
+ "," { return MoxiteTypes.COMMA; }
58
+ ":" { return MoxiteTypes.COLON; }
59
+ "|" { return MoxiteTypes.PIPE; }
60
+ "=" { return MoxiteTypes.EQ; }
61
+
62
+ "==="|!=="|=="|"!=" { return MoxiteTypes.EQUALITY_OP; }
63
+ "<="|">="|"<"|">" { return MoxiteTypes.COMPARE_OP; }
64
+
65
+ "of" { return MoxiteTypes.IDENTIFIER; } // Handled structurally in BNF
66
+ {IDENTIFIER} { return MoxiteTypes.IDENTIFIER; }
67
+ {NUMBER} { return MoxiteTypes.NUMBER; }
68
+ {STRING} { return MoxiteTypes.STRING; }
69
+
70
+ // Fallback for weird characters that are technically invalid but shouldn't crash the lexer
71
+ . { return TokenType.BAD_CHARACTER; }
72
+
73
+ // Handle unexpected newline to exit state if unclosed
74
+ \n { yybegin(YYINITIAL); return TokenType.WHITE_SPACE; }
75
+ }
76
+
77
+ <IN_INTERPOLATION> {
78
+ {WHITE_SPACE} { return TokenType.WHITE_SPACE; }
79
+ "}}" { yybegin(YYINITIAL); return MoxiteTypes.INTERP_END; }
80
+
81
+ "(" { return MoxiteTypes.LPAREN; }
82
+ ")" { return MoxiteTypes.RPAREN; }
83
+ "[" { return MoxiteTypes.LBRACKET; }
84
+ "]" { return MoxiteTypes.RBRACKET; }
85
+ "." { return MoxiteTypes.DOT; }
86
+ "," { return MoxiteTypes.COMMA; }
87
+ ":" { return MoxiteTypes.COLON; }
88
+ "|" { return MoxiteTypes.PIPE; }
89
+ "=" { return MoxiteTypes.EQ; }
90
+
91
+ "==="|!=="|=="|"!=" { return MoxiteTypes.EQUALITY_OP; }
92
+ "<="|">="|"<"|">" { return MoxiteTypes.COMPARE_OP; }
93
+
94
+ {IDENTIFIER} { return MoxiteTypes.IDENTIFIER; }
95
+ {NUMBER} { return MoxiteTypes.NUMBER; }
96
+ {STRING} { return MoxiteTypes.STRING; }
97
+
98
+ . { return TokenType.BAD_CHARACTER; }
99
+ }
100
+
101
+ [^] { return TokenType.BAD_CHARACTER; }
@@ -0,0 +1,79 @@
1
+ {
2
+ parserClass="tech.tsomaia.moxite.idea.parser.MoxiteParser"
3
+ parserUtilClass="tech.tsomaia.moxite.idea.parser.MoxiteParserUtil"
4
+
5
+ extends="com.intellij.extapi.psi.ASTWrapperPsiElement"
6
+
7
+ psiClassPrefix="Moxite"
8
+ psiImplClassSuffix="Impl"
9
+ psiPackage="tech.tsomaia.moxite.idea.psi"
10
+ psiImplPackage="tech.tsomaia.moxite.idea.psi.impl"
11
+
12
+ elementTypeHolderClass="tech.tsomaia.moxite.idea.psi.MoxiteTypes"
13
+ elementTypeClass="tech.tsomaia.moxite.idea.psi.MoxiteElementType"
14
+ tokenTypeClass="tech.tsomaia.moxite.idea.psi.MoxiteTokenType"
15
+
16
+ tokens = [
17
+ // These will be returned by the JFlex Lexer
18
+ TEXT="TEXT"
19
+ INTERP_START="{{"
20
+ INTERP_END="}}"
21
+
22
+ TAG_IF="@if"
23
+ TAG_ELSE_IF="@else if"
24
+ TAG_ELSE="@else"
25
+ TAG_ENDIF="@endif"
26
+
27
+ TAG_FOR="@for"
28
+ TAG_ENDFOR="@endfor"
29
+
30
+ TAG_CONST="@const"
31
+
32
+ LPAREN="("
33
+ RPAREN=")"
34
+ LBRACKET="["
35
+ RBRACKET="]"
36
+ DOT="."
37
+ COMMA=","
38
+ COLON=":"
39
+ PIPE="|"
40
+ EQ="="
41
+
42
+ EQUALITY_OP="regexp:(===|!==|==|!=)"
43
+ COMPARE_OP="regexp:(<|>|<=|>=)"
44
+
45
+ IDENTIFIER="regexp:[a-zA-Z_$][a-zA-Z0-9_$]*"
46
+ NUMBER="regexp:[0-9]+(\.[0-9]+)?"
47
+ STRING="regexp:\"[^\"]*\"|'[^']*'"
48
+ ]
49
+ }
50
+
51
+ moxiteFile ::= item_*
52
+
53
+ private item_ ::= ( textBlock | interpolation | ifBlock | forBlock | constBlock )
54
+
55
+ textBlock ::= TEXT
56
+
57
+ interpolation ::= INTERP_START expression INTERP_END
58
+
59
+ ifBlock ::= ifStatement elseIfStatement* elseStatement? TAG_ENDIF
60
+ ifStatement ::= TAG_IF LPAREN expression RPAREN item_*
61
+ elseIfStatement ::= TAG_ELSE_IF LPAREN expression RPAREN item_*
62
+ elseStatement ::= TAG_ELSE item_*
63
+
64
+ forBlock ::= TAG_FOR LPAREN IDENTIFIER IDENTIFIER /* 'of' */ expression RPAREN item_* TAG_ENDFOR
65
+
66
+ constBlock ::= TAG_CONST IDENTIFIER EQ expression
67
+
68
+ // Expression rules simplified for highlighting
69
+ expression ::= pipeExpression
70
+
71
+ pipeExpression ::= equalityExpression (PIPE IDENTIFIER (COLON equalityExpression (COMMA equalityExpression)* )? )*
72
+
73
+ equalityExpression ::= compareExpression (EQUALITY_OP compareExpression)*
74
+
75
+ compareExpression ::= primaryExpression (COMPARE_OP primaryExpression)*
76
+
77
+ primaryExpression ::= baseExpression ( DOT IDENTIFIER | LBRACKET pipeExpression RBRACKET )*
78
+
79
+ baseExpression ::= STRING | NUMBER | IDENTIFIER | LPAREN pipeExpression RPAREN
@@ -0,0 +1,39 @@
1
+ package tech.tsomaia.moxite.idea;
2
+
3
+ import com.intellij.openapi.fileTypes.LanguageFileType;
4
+ import org.jetbrains.annotations.NotNull;
5
+ import org.jetbrains.annotations.Nullable;
6
+
7
+ import javax.swing.*;
8
+
9
+ public class MoxiteFileType extends LanguageFileType {
10
+ public static final MoxiteFileType INSTANCE = new MoxiteFileType();
11
+
12
+ private MoxiteFileType() {
13
+ super(MoxiteLanguage.INSTANCE);
14
+ }
15
+
16
+ @NotNull
17
+ @Override
18
+ public String getName() {
19
+ return "Moxite Template";
20
+ }
21
+
22
+ @NotNull
23
+ @Override
24
+ public String getDescription() {
25
+ return "Moxite template logic format";
26
+ }
27
+
28
+ @NotNull
29
+ @Override
30
+ public String getDefaultExtension() {
31
+ return "mx";
32
+ }
33
+
34
+ @Nullable
35
+ @Override
36
+ public Icon getIcon() {
37
+ return MoxiteIcons.FILE;
38
+ }
39
+ }
@@ -0,0 +1,9 @@
1
+ package tech.tsomaia.moxite.idea;
2
+
3
+ import com.intellij.openapi.util.IconLoader;
4
+
5
+ import javax.swing.*;
6
+
7
+ public class MoxiteIcons {
8
+ public static final Icon FILE = IconLoader.getIcon("/icons/moxite.png", MoxiteIcons.class);
9
+ }
@@ -0,0 +1,11 @@
1
+ package tech.tsomaia.moxite.idea;
2
+
3
+ import com.intellij.lang.Language;
4
+
5
+ public class MoxiteLanguage extends Language {
6
+ public static final MoxiteLanguage INSTANCE = new MoxiteLanguage();
7
+
8
+ private MoxiteLanguage() {
9
+ super("Moxite");
10
+ }
11
+ }
@@ -0,0 +1,77 @@
1
+ package tech.tsomaia.moxite.idea;
2
+
3
+ import com.intellij.lang.ASTNode;
4
+ import com.intellij.lang.ParserDefinition;
5
+ import com.intellij.lang.PsiParser;
6
+ import com.intellij.lexer.Lexer;
7
+ import com.intellij.openapi.project.Project;
8
+ import com.intellij.psi.FileViewProvider;
9
+ import com.intellij.psi.PsiElement;
10
+ import com.intellij.psi.PsiFile;
11
+ import com.intellij.psi.TokenType;
12
+ import com.intellij.psi.tree.IFileElementType;
13
+ import com.intellij.psi.tree.TokenSet;
14
+ import org.jetbrains.annotations.NotNull;
15
+ import tech.tsomaia.moxite.idea.lexer.MoxiteLexerAdapter;
16
+ import tech.tsomaia.moxite.idea.parser.MoxiteParser;
17
+ import tech.tsomaia.moxite.idea.psi.MoxiteFile;
18
+ import tech.tsomaia.moxite.idea.psi.MoxiteTypes;
19
+
20
+ public class MoxiteParserDefinition implements ParserDefinition {
21
+
22
+ public static final IFileElementType FILE = new IFileElementType(MoxiteLanguage.INSTANCE);
23
+
24
+ @NotNull
25
+ @Override
26
+ public Lexer createLexer(Project project) {
27
+ return new MoxiteLexerAdapter();
28
+ }
29
+
30
+ @NotNull
31
+ @Override
32
+ public TokenSet getWhitespaceTokens() {
33
+ return TokenSet.create(TokenType.WHITE_SPACE);
34
+ }
35
+
36
+ @NotNull
37
+ @Override
38
+ public TokenSet getCommentTokens() {
39
+ return TokenSet.EMPTY; // no comments in this language yet
40
+ }
41
+
42
+ @NotNull
43
+ @Override
44
+ public TokenSet getStringLiteralElements() {
45
+ return TokenSet.create(MoxiteTypes.STRING);
46
+ }
47
+
48
+ @NotNull
49
+ @Override
50
+ public PsiParser createParser(Project project) {
51
+ return new MoxiteParser();
52
+ }
53
+
54
+ @NotNull
55
+ @Override
56
+ public IFileElementType getFileNodeType() {
57
+ return FILE;
58
+ }
59
+
60
+ @NotNull
61
+ @Override
62
+ public PsiFile createFile(@NotNull FileViewProvider viewProvider) {
63
+ return new MoxiteFile(viewProvider);
64
+ }
65
+
66
+ @NotNull
67
+ @Override
68
+ public SpaceRequirements spaceExistenceTypeBetweenTokens(ASTNode left, ASTNode right) {
69
+ return SpaceRequirements.MAY;
70
+ }
71
+
72
+ @NotNull
73
+ @Override
74
+ public PsiElement createElement(ASTNode node) {
75
+ return tech.tsomaia.moxite.idea.psi.MoxiteTypes.Factory.createElement(node);
76
+ }
77
+ }
@@ -0,0 +1,82 @@
1
+ package tech.tsomaia.moxite.idea;
2
+
3
+ import com.intellij.lexer.Lexer;
4
+ import com.intellij.openapi.editor.DefaultLanguageHighlighterColors;
5
+ import com.intellij.openapi.editor.colors.TextAttributesKey;
6
+ import com.intellij.openapi.fileTypes.SyntaxHighlighterBase;
7
+ import com.intellij.psi.tree.IElementType;
8
+ import org.jetbrains.annotations.NotNull;
9
+ import tech.tsomaia.moxite.idea.lexer.MoxiteLexerAdapter;
10
+ import tech.tsomaia.moxite.idea.psi.MoxiteTypes;
11
+
12
+ import static com.intellij.openapi.editor.colors.TextAttributesKey.createTextAttributesKey;
13
+
14
+ public class MoxiteSyntaxHighlighter extends SyntaxHighlighterBase {
15
+
16
+ public static final TextAttributesKey KEYWORD =
17
+ createTextAttributesKey("RELAY_KEYWORD", DefaultLanguageHighlighterColors.KEYWORD);
18
+ public static final TextAttributesKey STRING =
19
+ createTextAttributesKey("RELAY_STRING", DefaultLanguageHighlighterColors.STRING);
20
+ public static final TextAttributesKey NUMBER =
21
+ createTextAttributesKey("RELAY_NUMBER", DefaultLanguageHighlighterColors.NUMBER);
22
+ public static final TextAttributesKey IDENTIFIER =
23
+ createTextAttributesKey("RELAY_IDENTIFIER", DefaultLanguageHighlighterColors.IDENTIFIER);
24
+ public static final TextAttributesKey BRACES =
25
+ createTextAttributesKey("RELAY_BRACES", DefaultLanguageHighlighterColors.BRACES);
26
+ public static final TextAttributesKey OPERATOR =
27
+ createTextAttributesKey("RELAY_OPERATOR", DefaultLanguageHighlighterColors.OPERATION_SIGN);
28
+
29
+ private static final TextAttributesKey[] KEYWORD_KEYS = new TextAttributesKey[]{KEYWORD};
30
+ private static final TextAttributesKey[] STRING_KEYS = new TextAttributesKey[]{STRING};
31
+ private static final TextAttributesKey[] NUMBER_KEYS = new TextAttributesKey[]{NUMBER};
32
+ private static final TextAttributesKey[] IDENTIFIER_KEYS = new TextAttributesKey[]{IDENTIFIER};
33
+ private static final TextAttributesKey[] BRACES_KEYS = new TextAttributesKey[]{BRACES};
34
+ private static final TextAttributesKey[] OPERATOR_KEYS = new TextAttributesKey[]{OPERATOR};
35
+ private static final TextAttributesKey[] EMPTY_KEYS = new TextAttributesKey[0];
36
+
37
+ @NotNull
38
+ @Override
39
+ public Lexer getHighlightingLexer() {
40
+ return new MoxiteLexerAdapter();
41
+ }
42
+
43
+ @NotNull
44
+ @Override
45
+ public TextAttributesKey[] getTokenHighlights(IElementType tokenType) {
46
+ if (tokenType.equals(MoxiteTypes.TAG_IF) ||
47
+ tokenType.equals(MoxiteTypes.TAG_ELSE_IF) ||
48
+ tokenType.equals(MoxiteTypes.TAG_ELSE) ||
49
+ tokenType.equals(MoxiteTypes.TAG_ENDIF) ||
50
+ tokenType.equals(MoxiteTypes.TAG_FOR) ||
51
+ tokenType.equals(MoxiteTypes.TAG_ENDFOR) ||
52
+ tokenType.equals(MoxiteTypes.TAG_CONST)) {
53
+ return KEYWORD_KEYS;
54
+ }
55
+
56
+ if (tokenType.equals(MoxiteTypes.INTERP_START) ||
57
+ tokenType.equals(MoxiteTypes.INTERP_END)) {
58
+ return BRACES_KEYS;
59
+ }
60
+
61
+ if (tokenType.equals(MoxiteTypes.STRING)) {
62
+ return STRING_KEYS;
63
+ }
64
+
65
+ if (tokenType.equals(MoxiteTypes.NUMBER)) {
66
+ return NUMBER_KEYS;
67
+ }
68
+
69
+ if (tokenType.equals(MoxiteTypes.IDENTIFIER)) {
70
+ return IDENTIFIER_KEYS;
71
+ }
72
+
73
+ if (tokenType.equals(MoxiteTypes.EQUALITY_OP) ||
74
+ tokenType.equals(MoxiteTypes.COMPARE_OP) ||
75
+ tokenType.equals(MoxiteTypes.EQ) ||
76
+ tokenType.equals(MoxiteTypes.PIPE)) {
77
+ return OPERATOR_KEYS;
78
+ }
79
+
80
+ return EMPTY_KEYS;
81
+ }
82
+ }
@@ -0,0 +1,16 @@
1
+ package tech.tsomaia.moxite.idea;
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 MoxiteSyntaxHighlighterFactory extends SyntaxHighlighterFactory {
11
+ @NotNull
12
+ @Override
13
+ public SyntaxHighlighter getSyntaxHighlighter(@Nullable Project project, @Nullable VirtualFile virtualFile) {
14
+ return new MoxiteSyntaxHighlighter();
15
+ }
16
+ }
@@ -0,0 +1,9 @@
1
+ package tech.tsomaia.moxite.idea.lexer;
2
+
3
+ import com.intellij.lexer.FlexAdapter;
4
+
5
+ public class MoxiteLexerAdapter extends FlexAdapter {
6
+ public MoxiteLexerAdapter() {
7
+ super(new _MoxiteLexer(null));
8
+ }
9
+ }
@@ -0,0 +1,6 @@
1
+ package tech.tsomaia.moxite.idea.parser;
2
+
3
+ import com.intellij.lang.parser.GeneratedParserUtilBase;
4
+
5
+ public class MoxiteParserUtil extends GeneratedParserUtilBase {
6
+ }
@@ -0,0 +1,12 @@
1
+ package tech.tsomaia.moxite.idea.psi;
2
+
3
+ import com.intellij.psi.tree.IElementType;
4
+ import org.jetbrains.annotations.NonNls;
5
+ import org.jetbrains.annotations.NotNull;
6
+ import tech.tsomaia.moxite.idea.MoxiteLanguage;
7
+
8
+ public class MoxiteElementType extends IElementType {
9
+ public MoxiteElementType(@NotNull @NonNls String debugName) {
10
+ super(debugName, MoxiteLanguage.INSTANCE);
11
+ }
12
+ }
@@ -0,0 +1,25 @@
1
+ package tech.tsomaia.moxite.idea.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.jetbrains.annotations.NotNull;
7
+ import tech.tsomaia.moxite.idea.MoxiteFileType;
8
+ import tech.tsomaia.moxite.idea.MoxiteLanguage;
9
+
10
+ public class MoxiteFile extends PsiFileBase {
11
+ public MoxiteFile(@NotNull FileViewProvider viewProvider) {
12
+ super(viewProvider, MoxiteLanguage.INSTANCE);
13
+ }
14
+
15
+ @NotNull
16
+ @Override
17
+ public FileType getFileType() {
18
+ return MoxiteFileType.INSTANCE;
19
+ }
20
+
21
+ @Override
22
+ public String toString() {
23
+ return "Moxite File";
24
+ }
25
+ }
@@ -0,0 +1,17 @@
1
+ package tech.tsomaia.moxite.idea.psi;
2
+
3
+ import com.intellij.psi.tree.IElementType;
4
+ import org.jetbrains.annotations.NonNls;
5
+ import org.jetbrains.annotations.NotNull;
6
+ import tech.tsomaia.moxite.idea.MoxiteLanguage;
7
+
8
+ public class MoxiteTokenType extends IElementType {
9
+ public MoxiteTokenType(@NotNull @NonNls String debugName) {
10
+ super(debugName, MoxiteLanguage.INSTANCE);
11
+ }
12
+
13
+ @Override
14
+ public String toString() {
15
+ return "MoxiteTokenType." + super.toString();
16
+ }
17
+ }
@@ -0,0 +1,32 @@
1
+ <idea-plugin>
2
+ <id>tech.tsomaia.moxite.idea</id>
3
+ <name>Moxite Template Support</name>
4
+ <vendor email="admin@tsomaia.tech" url="https://tsomaia.tech">Tornike Tsomaia</vendor>
5
+
6
+ <description><![CDATA[
7
+ Provides language support for the Moxite Template Engine.
8
+ Includes full syntax highlighting, lexing, and semantic parsing for .mx files.
9
+ ]]></description>
10
+
11
+ <!-- please see https://plugins.jetbrains.com/docs/intellij/plugin-compatibility.html
12
+ on how to target different products -->
13
+ <depends>com.intellij.modules.platform</depends>
14
+
15
+ <extensions defaultExtensionNs="com.intellij">
16
+ <fileType
17
+ name="Moxite Template"
18
+ implementationClass="tech.tsomaia.moxite.idea.MoxiteFileType"
19
+ fieldName="INSTANCE"
20
+ language="Moxite"
21
+ extensions="mx"
22
+ />
23
+ <lang.parserDefinition
24
+ language="Moxite"
25
+ implementationClass="tech.tsomaia.moxite.idea.MoxiteParserDefinition"
26
+ />
27
+ <lang.syntaxHighlighterFactory
28
+ language="Moxite"
29
+ implementationClass="tech.tsomaia.moxite.idea.MoxiteSyntaxHighlighterFactory"
30
+ />
31
+ </extensions>
32
+ </idea-plugin>