feat(mpp-idea): add NanoDSL language support with syntax highlighting#523
feat(mpp-idea): add NanoDSL language support with syntax highlighting#523
Conversation
- Add nanodsl-lang module with complete lexer and parser definitions - Implement syntax highlighting for NanoDSL keywords, components, operators - Support for .nanodsl file extension - Highlight keywords: component, state, request, if, for, in, content - Highlight layout components: VStack, HStack, Card, SplitView, etc. - Highlight display components: Text, Image, Badge, Alert, etc. - Highlight input components: Button, Input, TextArea, Select, etc. - Highlight operators: << (bind read), := (bind write), +=, -=, *=, /= - Highlight actions: Navigate, Fetch, ShowToast, StateMutation - Highlight event handlers: on_click, on_success, on_error, etc. - Highlight HTTP methods: GET, POST, PUT, PATCH, DELETE - Highlight types: int, float, string, bool
WalkthroughThis pull request introduces a new Gradle subproject Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🤖 Augment PR SummarySummary: Adds first-class NanoDSL support to the AutoDev IntelliJ plugin so NanoDSL files can be recognized and highlighted. Changes:
Technical Notes: Uses GrammarKit tasks ( 🤖 Was this summary useful? React with 👍 or 👎 |
| companion object { | ||
| val FILE = IFileElementType(cc.unitmesh.nanodsl.language.NanoDSLLanguage) | ||
| val COMMENTS = TokenSet.create(NanoDSLTypes.COMMENT) | ||
| val WHITESPACES = TokenSet.EMPTY |
There was a problem hiding this comment.
getWhitespaceTokens() currently returns TokenSet.EMPTY, so TokenType.WHITE_SPACE may not be skipped during parsing and most real NanoDSL files (with spaces/indentation) can fail to parse. devins-lang includes TokenType.WHITE_SPACE via ShireTokenTypeSets.WHITESPACES.
🤖 Was this useful? React with 👍 or 👎
|
|
||
| requestEntry ::= (URL | METHOD | BODY | HEADERS | ON_SUCCESS | ON_ERROR) COLON expr NEWLINE | ||
|
|
||
| propertyAssignment ::= IDENTIFIER COLON expr NEWLINE |
There was a problem hiding this comment.
propertyAssignment only allows IDENTIFIER as the key, but the lexer emits dedicated tokens for common keys like on_click/content/method/url, so the PR’s example (e.g., on_click: ..., Text(content << ...)) likely won’t parse and will show errors. This looks like a lexer/parser token-mismatch rather than an input issue.
🤖 Was this useful? React with 👍 or 👎
|
|
||
| forBlock ::= FOR IDENTIFIER IN expr COLON NEWLINE componentInstances | ||
|
|
||
| expr ::= STRING | NUMBER | BOOLEAN | IDENTIFIER (DOT IDENTIFIER)* | actionCall | binaryExpr |
There was a problem hiding this comment.
In expr, the IDENTIFIER (DOT IDENTIFIER)* alternative appears before binaryExpr, so input like state.count += 1 will likely be consumed as just state.count (leaving += 1), causing parse errors in places expecting a full expression. This makes assignment-style operators hard to parse reliably with the current rule ordering.
🤖 Was this useful? React with 👍 or 👎
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (4)
mpp-idea/mpp-idea-exts/nanodsl-lang/src/grammar/NanoDSLLexer.flex (1)
25-25: Remove unused pattern definitions.The
SPACE(line 25) andINDENT(line 33) patterns are defined but never referenced in the token matching rules section (lines 132-242). Since onlyWHITE_SPACE,NEWLINE, andCOMMENTare used for whitespace handling, these unused patterns should be removed to improve code clarity.🔎 Proposed fix
-SPACE = [ \t\n\x0B\f\r]+ IDENTIFIER = [a-zA-Z_][a-zA-Z0-9_]* NUMBER = [0-9]+(\.[0-9]+)? BOOLEAN = true|false STRING = \"([^\\\"\r\n]|\\[^\r\n])*\" COMMENT = #[^\r\n]* NEWLINE = \n | \r | \r\n WHITE_SPACE = [ \t]+ -INDENT = [ ][ ][ ][ ]+Also applies to: 33-33
mpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/highlight/NanoDSLSyntaxHighlighterFactory.kt (1)
7-9: LGTM! Standard factory implementation.The factory correctly instantiates and returns the syntax highlighter. The current approach of creating a new instance is standard practice in IntelliJ plugins and works well.
Optionally, since
NanoDSLSyntaxHighlighteris stateless, you could optimize by using a singleton instance:🔎 Optional singleton optimization
class NanoDSLSyntaxHighlighterFactory : SyntaxHighlighterFactory() { - override fun getSyntaxHighlighter(project: Project?, virtualFile: VirtualFile?) = NanoDSLSyntaxHighlighter() + override fun getSyntaxHighlighter(project: Project?, virtualFile: VirtualFile?) = INSTANCE + + companion object { + private val INSTANCE = NanoDSLSyntaxHighlighter() + } }mpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/NanoDSLFileType.kt (1)
6-14: Consider adding a file type icon.The implementation is correct, but returning
nullforgetIcon()means.nanodslfiles won't have a distinctive icon in the Project view. Consider adding a custom icon to improve discoverability.This is optional and can be addressed in a follow-up.
mpp-idea/build.gradle.kts (1)
897-943: Remove unusedkotlin.serializationplugin.The
org.jetbrains.kotlin.plugin.serializationplugin is applied but has no usage in the nanodsl-lang module—no@Serializableannotations orkotlinx.serializationimports exist. Consider removing it to reduce build complexity, following the pattern where only modules with actual serialization needs (like devins-lang) apply the plugin.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (56)
mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/lexer/_NanoDSLLexer.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/parser/NanoDSLParser.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/NanoDSLActionCall.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/NanoDSLActionName.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/NanoDSLArg.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/NanoDSLArgList.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/NanoDSLBinaryExpr.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/NanoDSLComponentBody.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/NanoDSLComponentDecl.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/NanoDSLComponentInstance.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/NanoDSLComponentInstances.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/NanoDSLComponentName.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/NanoDSLContentBlock.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/NanoDSLExpr.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/NanoDSLForBlock.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/NanoDSLIfBlock.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/NanoDSLParam.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/NanoDSLParamList.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/NanoDSLProp.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/NanoDSLPropList.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/NanoDSLPropertyAssignment.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/NanoDSLRequestBlock.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/NanoDSLRequestEntries.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/NanoDSLRequestEntry.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/NanoDSLStateBlock.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/NanoDSLStateEntries.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/NanoDSLStateEntry.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/NanoDSLTypeRef.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/NanoDSLTypes.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/NanoDSLVisitor.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/impl/NanoDSLActionCallImpl.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/impl/NanoDSLActionNameImpl.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/impl/NanoDSLArgImpl.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/impl/NanoDSLArgListImpl.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/impl/NanoDSLBinaryExprImpl.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/impl/NanoDSLComponentBodyImpl.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/impl/NanoDSLComponentDeclImpl.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/impl/NanoDSLComponentInstanceImpl.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/impl/NanoDSLComponentInstancesImpl.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/impl/NanoDSLComponentNameImpl.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/impl/NanoDSLContentBlockImpl.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/impl/NanoDSLExprImpl.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/impl/NanoDSLForBlockImpl.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/impl/NanoDSLIfBlockImpl.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/impl/NanoDSLParamImpl.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/impl/NanoDSLParamListImpl.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/impl/NanoDSLPropImpl.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/impl/NanoDSLPropListImpl.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/impl/NanoDSLPropertyAssignmentImpl.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/impl/NanoDSLRequestBlockImpl.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/impl/NanoDSLRequestEntriesImpl.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/impl/NanoDSLRequestEntryImpl.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/impl/NanoDSLStateBlockImpl.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/impl/NanoDSLStateEntriesImpl.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/impl/NanoDSLStateEntryImpl.javais excluded by!**/gen/**mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/psi/impl/NanoDSLTypeRefImpl.javais excluded by!**/gen/**
📒 Files selected for processing (14)
mpp-idea/build.gradle.kts(3 hunks)mpp-idea/mpp-idea-exts/nanodsl-lang/src/grammar/NanoDSLLexer.flex(1 hunks)mpp-idea/mpp-idea-exts/nanodsl-lang/src/grammar/NanoDSLParser.bnf(1 hunks)mpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/NanoDSLFileType.kt(1 hunks)mpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/NanoDSLLanguage.kt(1 hunks)mpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/highlight/NanoDSLSyntaxHighlighter.kt(1 hunks)mpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/highlight/NanoDSLSyntaxHighlighterFactory.kt(1 hunks)mpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/lexer/NanoDSLLexerAdapter.kt(1 hunks)mpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/lexer/NanoDSLTokenType.kt(1 hunks)mpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/parser/NanoDSLParserDefinition.kt(1 hunks)mpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/psi/NanoDSLElementType.kt(1 hunks)mpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/psi/NanoDSLFile.kt(1 hunks)mpp-idea/mpp-idea-exts/nanodsl-lang/src/main/resources/cc.unitmesh.nanodsl.language.xml(1 hunks)mpp-idea/settings.gradle.kts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.kt
📄 CodeRabbit inference engine (AGENTS.md)
**/*.kt: Useexpect/actualpattern for platform-specific code (for example:Platformimplementations)
Use concrete classes (not interfaces) andPromise(notFlow) for @JsExport annotations
Avoid emoji and UTF-8 characters in WASM code
When modifyingCodingAgentRenderer, update ALL implementations:DefaultCodingAgentRenderer,ComposeRenderer,JewelRenderer,ServerSideRenderer, andJsRendererAdapterin Kotlin
When modifyingCodingAgentRenderer, update JVM CLI implementations:CodingCliRendererandConsoleRenderer
UseAutoDevColorsorMaterialTheme.colorSchemefor Compose styling
Files:
mpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/psi/NanoDSLElementType.ktmpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/lexer/NanoDSLTokenType.ktmpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/NanoDSLFileType.ktmpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/parser/NanoDSLParserDefinition.ktmpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/highlight/NanoDSLSyntaxHighlighter.ktmpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/lexer/NanoDSLLexerAdapter.ktmpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/NanoDSLLanguage.ktmpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/psi/NanoDSLFile.ktmpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/highlight/NanoDSLSyntaxHighlighterFactory.kt
**/*.{kt,ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Never use hardcoded colors - always use design tokens from the design system
Files:
mpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/psi/NanoDSLElementType.ktmpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/lexer/NanoDSLTokenType.ktmpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/NanoDSLFileType.ktmpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/parser/NanoDSLParserDefinition.ktmpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/highlight/NanoDSLSyntaxHighlighter.ktmpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/lexer/NanoDSLLexerAdapter.ktmpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/NanoDSLLanguage.ktmpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/psi/NanoDSLFile.ktmpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/highlight/NanoDSLSyntaxHighlighterFactory.kt
🧠 Learnings (1)
📚 Learning: 2025-12-07T08:40:10.859Z
Learnt from: CR
Repo: phodal/auto-dev PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-07T08:40:10.859Z
Learning: Run `./gradlew :mpp-ui:generateI18n4kFiles` command when updating i18n resources
Applied to files:
mpp-idea/build.gradle.kts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: CodeQL analysis (java)
- GitHub Check: Build
🔇 Additional comments (17)
mpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/lexer/NanoDSLLexerAdapter.kt (1)
1-5: LGTM! Standard IntelliJ lexer adapter pattern.The implementation correctly wraps the JFlex-generated
_NanoDSLLexerusingFlexAdapter, following IntelliJ Platform conventions for language plugin development. The adapter will properly integrate the lexer with the syntax highlighter and parser definition.mpp-idea/mpp-idea-exts/nanodsl-lang/src/grammar/NanoDSLLexer.flex (1)
238-239: Good practice: IDENTIFIER rule correctly positioned.The
IDENTIFIERrule is correctly placed as the last pattern match before the fallback rule. This ensures that all keywords and component names are matched first, preventing them from being incorrectly tokenized as identifiers.mpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/highlight/NanoDSLSyntaxHighlighter.kt (3)
12-17: LGTM! Standard IntelliJ syntax highlighter pattern.The class correctly extends
SyntaxHighlighterBaseand implements the required methods following IntelliJ platform conventions.
23-128: Excellent token categorization!The token sets are well-organized into semantic categories (keywords, event handlers, components, actions, etc.) and comprehensively cover the NanoDSL syntax elements described in the PR objectives.
130-156: Well-structured color mappings using design system tokens.The attribute mappings appropriately use
DefaultLanguageHighlighterColorsdesign tokens throughout, following the coding guideline to avoid hardcoded colors. Semantic mappings are sensible: keywords as KEYWORD, components as CLASS_NAME, actions as STATIC_METHOD, etc.Also applies to: 162-181
mpp-idea/mpp-idea-exts/nanodsl-lang/src/main/resources/cc.unitmesh.nanodsl.language.xml (1)
1-26: LGTM! Properly configured plugin descriptor.The plugin descriptor correctly registers all necessary extension points for NanoDSL language support:
- File type association for
.nanodslextension- Parser definition for language parsing
- Syntax highlighter factory for editor highlighting
The structure follows standard IntelliJ plugin conventions.
mpp-idea/settings.gradle.kts (1)
37-38: LGTM!The new
nanodsl-langmodule is correctly added to the extension modules include block, following the established pattern.mpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/lexer/NanoDSLTokenType.kt (1)
1-9: LGTM!Standard IntelliJ token type implementation. The
toString()override provides helpful debugging context by prefixing with the class name.mpp-idea/build.gradle.kts (1)
222-222: LGTM!The resource inclusion and dependency wiring for
nanodsl-langfollows the established pattern used by other extension modules.Also applies to: 335-335
mpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/psi/NanoDSLElementType.kt (1)
1-7: LGTM!Standard IntelliJ PSI element type implementation correctly bound to
NanoDSLLanguage.mpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/NanoDSLLanguage.kt (1)
1-8: LGTM!Standard IntelliJ language singleton implementation. The
getDisplayName()override is technically redundant since theLanguagebase class uses the constructor parameter as the display name by default, but keeping it explicit is fine for clarity.mpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/psi/NanoDSLFile.kt (1)
1-14: LGTM!Standard IntelliJ PSI file implementation correctly wiring
NanoDSLLanguageandNanoDSLFileType.mpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/parser/NanoDSLParserDefinition.kt (1)
24-38: LGTM!The parser definition methods are correctly implemented, properly wiring the lexer adapter, generated parser, and PSI factory.
mpp-idea/mpp-idea-exts/nanodsl-lang/src/grammar/NanoDSLParser.bnf (4)
1-13: LGTM! Parser metadata is properly configured.The Grammar-Kit configuration follows IntelliJ conventions with appropriate package structure and PSI generation settings.
110-138: LGTM! Basic grammar structure is well-defined.The root rule and structural elements (components, state blocks, request blocks) are clearly defined with proper handling of significant whitespace.
17-17: The cross-platform newline handling is already correctly implemented in the actual JFlex lexer (NanoDSLLexer.flex), which definesNEWLINE = \n | \r | \r\n. The BNF pattern is only for IDE preview purposes and does not affect the production lexer's behavior. No changes needed.Likely an incorrect or invalid review comment.
164-164: The expr rule grammar is unambiguous and correctly handles its intended design.The
exprrule's alternatives (STRING, NUMBER, BOOLEAN, IDENTIFIER with optional DOT chains, actionCall, binaryExpr) are sufficiently distinguishable without requiring additional precedence levels. ThebinaryExprrule supports only compound assignment operators (+=, -=, *=, /=), which are explicit and non-overlapping. PEG's ordered choice correctly resolves the alternatives without ambiguity.The suggested test case (
count += state.value + 1) cannot parse because the DSL does not define the+operator—only compound assignments (+=, -=, *=, /=) are supported. Separating expression levels is unnecessary for the current design.Likely an incorrect or invalid review comment.
| %class NanoDSLLexer | ||
| %class _NanoDSLLexer |
There was a problem hiding this comment.
Remove duplicate %class directive - JFlex syntax error.
JFlex only allows one %class directive per lexer specification. Having both %class NanoDSLLexer and %class _NanoDSLLexer is invalid syntax.
Based on standard IntelliJ plugin patterns, you should keep only %class _NanoDSLLexer (the generated class with underscore prefix), and create a separate Kotlin/Java wrapper class named NanoDSLLexer that adapts the generated lexer.
🔎 Proposed fix
-%class NanoDSLLexer
%class _NanoDSLLexer📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| %class NanoDSLLexer | |
| %class _NanoDSLLexer | |
| %class _NanoDSLLexer |
🤖 Prompt for AI Agents
In mpp-idea/mpp-idea-exts/nanodsl-lang/src/grammar/NanoDSLLexer.flex around
lines 16-17, remove the duplicate %class directive so only %class _NanoDSLLexer
remains (JFlex permits a single %class). Then create or keep a separate
Kotlin/Java wrapper class named NanoDSLLexer that instantiates and delegates to
the generated _NanoDSLLexer (adapting constructors/method signatures as needed)
so external code uses NanoDSLLexer while the generated lexer class keeps the
underscore name.
| componentInstance ::= componentName propList? COLON NEWLINE componentBody | ||
| | componentName LPAREN argList? RPAREN COLON? NEWLINE? componentBody? | ||
| | componentName LPAREN argList? RPAREN NEWLINE? |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "NanoDSLParser.bnf" -type fRepository: phodal/auto-dev
Length of output: 126
🏁 Script executed:
fd "NanoDSLParser" --type fRepository: phodal/auto-dev
Length of output: 493
🏁 Script executed:
find . -path "*nanodsl*" -o -path "*nano-dsl*" | grep -E "\.(bnf|test|sample)" | head -20Repository: phodal/auto-dev
Length of output: 126
🏁 Script executed:
cat -n ./mpp-idea/mpp-idea-exts/nanodsl-lang/src/grammar/NanoDSLParser.bnf | sed -n '130,155p'Repository: phodal/auto-dev
Length of output: 1344
🏁 Script executed:
wc -l ./mpp-idea/mpp-idea-exts/nanodsl-lang/src/grammar/NanoDSLParser.bnfRepository: phodal/auto-dev
Length of output: 130
🏁 Script executed:
cat ./mpp-idea/mpp-idea-exts/nanodsl-lang/src/grammar/NanoDSLParser.bnf | head -150 | tail -30Repository: phodal/auto-dev
Length of output: 1289
🏁 Script executed:
cat ./xiuper-ui/src/jvmTest/kotlin/cc/unitmesh/xuiper/dsl/NanoDSLParserTest.kt | grep -A 10 -B 2 "Button\|componentInstance\|LPAREN"Repository: phodal/auto-dev
Length of output: 5842
🏁 Script executed:
cat ./xiuper-ui/src/commonTest/kotlin/cc/unitmesh/xuiper/dsl/NanoDSLParserCommonTest.kt | head -100Repository: phodal/auto-dev
Length of output: 2579
🏁 Script executed:
grep -A 5 -B 5 "componentInstance" ./mpp-idea/mpp-idea-exts/nanodsl-lang/src/grammar/NanoDSLParser.bnfRepository: phodal/auto-dev
Length of output: 1412
Line 142 is redundant and unreachable due to rule ordering.
The second alternative at line 141 (componentName LPAREN argList? RPAREN COLON? NEWLINE? componentBody?) will always match inputs that line 142 attempts to match, because all its elements are optional. Since PEG uses ordered choice with first-match semantics, where the first alternative that succeeds is used and subsequent alternatives are ignored, line 142 will never be reached. Remove the redundant third alternative.
🤖 Prompt for AI Agents
In mpp-idea/mpp-idea-exts/nanodsl-lang/src/grammar/NanoDSLParser.bnf around
lines 140 to 142, the third alternative (line 142) is redundant and unreachable
because the preceding alternative (line 141) has optional COLON, NEWLINE and
componentBody, which will always match the same inputs first under PEG ordered
choice; remove the third alternative so the rule reads only the first two
alternatives (componentName propList? COLON NEWLINE componentBody and
componentName LPAREN argList? RPAREN COLON? NEWLINE? componentBody?) to
eliminate dead/unreachable grammar.
|
|
||
| actionName ::= NAVIGATE | FETCH | SHOWTOAST | STATEMUTATION | ||
|
|
||
| binaryExpr ::= expr (PLUS_EQUALS | MINUS_EQUALS | TIMES_EQUALS | DIV_EQUALS) expr |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cd mpp-idea/mpp-idea-exts/nanodsl-lang && find . -name "*.bnf" -o -name "*.flex" | head -20Repository: phodal/auto-dev
Length of output: 122
🏁 Script executed:
cat -n mpp-idea/mpp-idea-exts/nanodsl-lang/src/grammar/NanoDSLParser.bnf | sed -n '150,180p'Repository: phodal/auto-dev
Length of output: 825
🏁 Script executed:
grep -n "PLUS\|MINUS\|TIMES\|DIV\|expr" mpp-idea/mpp-idea-exts/nanodsl-lang/src/grammar/NanoDSLParser.bnfRepository: phodal/auto-dev
Length of output: 1015
🏁 Script executed:
cat -n mpp-idea/mpp-idea-exts/nanodsl-lang/src/grammar/NanoDSLParser.bnf | head -80Repository: phodal/auto-dev
Length of output: 3376
🏁 Script executed:
grep -E "PLUS[^_]|MINUS[^_]|TIMES[^_]|DIV[^_]|\s\+\s|\s-\s|\s\*\s|\s/\s" mpp-idea/mpp-idea-exts/nanodsl-lang/src/grammar/NanoDSLParser.bnfRepository: phodal/auto-dev
Length of output: 197
🏁 Script executed:
cat -n mpp-idea/mpp-idea-exts/nanodsl-lang/src/grammar/NanoDSLParser.bnf | tail -20Repository: phodal/auto-dev
Length of output: 764
🏁 Script executed:
cat -n mpp-idea/mpp-idea-exts/nanodsl-lang/src/grammar/NanoDSLLexer.flexRepository: phodal/auto-dev
Length of output: 9693
Clarify design intent: binaryExpr currently lacks arithmetic operators.
The binaryExpr rule at line 170 contains only compound assignment operators (+=, -=, *=, /=). Simple arithmetic operators (+, -, *, /) are not defined in the grammar at all. This means NanoDSL cannot perform basic arithmetic operations like a + b—only compound assignments like a += b.
Issues:
- The rule name
binaryExpris misleading; it should reflect that it handles only compound assignment - If arithmetic operations are required, define and add arithmetic operator tokens (PLUS, MINUS, TIMES, DIV) and extend the rule accordingly
- If arithmetic is intentionally excluded, clarify this design decision and rename the rule to avoid confusion
🤖 Prompt for AI Agents
In mpp-idea/mpp-idea-exts/nanodsl-lang/src/grammar/NanoDSLParser.bnf around line
170, the binaryExpr rule only includes compound-assignment operators
(PLUS_EQUALS, MINUS_EQUALS, TIMES_EQUALS, DIV_EQUALS) which prevents simple
arithmetic expressions and makes the rule name misleading; either rename the
rule to indicate it handles only compound assignments (e.g., compoundAssignExpr)
or extend it to include simple arithmetic by adding tokens for PLUS, MINUS,
TIMES, DIV and allowing expr (
(PLUS|MINUS|TIMES|DIV|PLUS_EQUALS|MINUS_EQUALS|TIMES_EQUALS|DIV_EQUALS) expr )
so both binary arithmetic and compound assignment are supported—choose and apply
one of these two fixes and update any references/docs accordingly.
| // Literals | ||
| ATTRIBUTES[NanoDSLTypes.STRING] = DefaultLanguageHighlighterColors.STRING | ||
| ATTRIBUTES[NanoDSLTypes.NUMBER] = DefaultLanguageHighlighterColors.NUMBER | ||
| ATTRIBUTES[NanoDSLTypes.BOOLEAN] = DefaultLanguageHighlighterColors.KEYWORD |
There was a problem hiding this comment.
Consider using a more appropriate color for boolean literals.
Boolean literals (true/false) are typically styled as literals rather than keywords. While the current mapping works, consider using DefaultLanguageHighlighterColors.NUMBER or keeping the default literal styling for semantic consistency.
🔎 Suggested alternative
// Literals
ATTRIBUTES[NanoDSLTypes.STRING] = DefaultLanguageHighlighterColors.STRING
ATTRIBUTES[NanoDSLTypes.NUMBER] = DefaultLanguageHighlighterColors.NUMBER
-ATTRIBUTES[NanoDSLTypes.BOOLEAN] = DefaultLanguageHighlighterColors.KEYWORD
+ATTRIBUTES[NanoDSLTypes.BOOLEAN] = DefaultLanguageHighlighterColors.NUMBER📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Literals | |
| ATTRIBUTES[NanoDSLTypes.STRING] = DefaultLanguageHighlighterColors.STRING | |
| ATTRIBUTES[NanoDSLTypes.NUMBER] = DefaultLanguageHighlighterColors.NUMBER | |
| ATTRIBUTES[NanoDSLTypes.BOOLEAN] = DefaultLanguageHighlighterColors.KEYWORD | |
| // Literals | |
| ATTRIBUTES[NanoDSLTypes.STRING] = DefaultLanguageHighlighterColors.STRING | |
| ATTRIBUTES[NanoDSLTypes.NUMBER] = DefaultLanguageHighlighterColors.NUMBER | |
| ATTRIBUTES[NanoDSLTypes.BOOLEAN] = DefaultLanguageHighlighterColors.NUMBER |
🤖 Prompt for AI Agents
In
mpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/highlight/NanoDSLSyntaxHighlighter.kt
around lines 157-160, the BOOLEAN token is mapped to
DefaultLanguageHighlighterColors.KEYWORD; change this mapping to a literal-style
color (for example DefaultLanguageHighlighterColors.NUMBER or
DefaultLanguageHighlighterColors.STRING) so boolean literals are highlighted as
literals rather than keywords, update the ATTRIBUTES[NanoDSLTypes.BOOLEAN]
assignment accordingly and ensure imports remain valid.
| class NanoDSLParserDefinition : ParserDefinition { | ||
| companion object { | ||
| val FILE = IFileElementType(cc.unitmesh.nanodsl.language.NanoDSLLanguage) | ||
| val COMMENTS = TokenSet.create(NanoDSLTypes.COMMENT) | ||
| val WHITESPACES = TokenSet.EMPTY | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check the NanoDSL lexer definition
echo "=== Checking for NanoDSL lexer files ==="
find mpp-idea/mpp-idea-exts/nanodsl-lang -type f -name "*Lexer*" | head -10
echo ""
echo "=== Checking for grammar files ==="
find mpp-idea/mpp-idea-exts/nanodsl-lang -type f -name "*.flex" -o -name "*.bnf" | head -10
echo ""
echo "=== Looking at NanoDSLParserDefinition.kt ==="
cat mpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/parser/NanoDSLParserDefinition.ktRepository: phodal/auto-dev
Length of output: 2083
🏁 Script executed:
#!/bin/bash
# Check devins-lang ParserDefinition for whitespace handling
echo "=== Checking devins-lang ParserDefinition ==="
find mpp-idea/mpp-idea-exts/devins-lang -type f -name "*ParserDefinition*" -o -name "*Parser.kt" | head -5
echo ""
echo "=== Looking at devins-lang implementations ==="
rg "WHITESPACE|TokenSet.EMPTY" mpp-idea/mpp-idea-exts/devins-lang/src/main/kotlin/ --type kt -B2 -A2 2>/dev/null | head -50Repository: phodal/auto-dev
Length of output: 479
🏁 Script executed:
#!/bin/bash
# Check the NanoDSL lexer definition
echo "=== Checking NanoDSLLexer.flex ==="
cat mpp-idea/mpp-idea-exts/nanodsl-lang/src/grammar/NanoDSLLexer.flex
echo ""
echo "=== Checking for whitespace handling in generated lexer ==="
head -150 mpp-idea/mpp-idea-exts/nanodsl-lang/src/gen/cc/unitmesh/nanodsl/language/lexer/_NanoDSLLexer.java | grep -A5 -B5 "WHITE_SPACE"Repository: phodal/auto-dev
Length of output: 8088
🏁 Script executed:
#!/bin/bash
# Check DevInParserDefinition for comparison
echo "=== Checking DevInParserDefinition.kt ==="
cat mpp-idea/mpp-idea-exts/devins-lang/src/main/kotlin/cc/unitmesh/devti/language/parser/DevInParserDefinition.kt
echo ""
echo "=== Check DevinsLexer.flex ==="
find mpp-idea/mpp-idea-exts/devins-lang -name "*Lexer.flex" -exec cat {} \;Repository: phodal/auto-dev
Length of output: 21745
Declare whitespace tokens in getWhitespaceTokens() to match lexer output.
The NanoDSLLexer returns TokenType.WHITE_SPACE for whitespace, but the ParserDefinition declares WHITESPACES = TokenSet.EMPTY. Token types treated as whitespace by the PSI builder are automatically skipped by PsiBuilder. Change to TokenSet.create(TokenType.WHITE_SPACE) to properly handle whitespace tokens.
🤖 Prompt for AI Agents
In
mpp-idea/mpp-idea-exts/nanodsl-lang/src/main/kotlin/cc/unitmesh/nanodsl/language/parser/NanoDSLParserDefinition.kt
around lines 17 to 22, the parser currently declares WHITESPACES =
TokenSet.EMPTY but the lexer emits TokenType.WHITE_SPACE; update WHITESPACES to
include TokenType.WHITE_SPACE (e.g., TokenSet.create(TokenType.WHITE_SPACE)) so
the PSI builder treats lexer whitespace as skipped tokens, and ensure any
necessary import for TokenType is present.
There was a problem hiding this comment.
Pull request overview
This PR adds comprehensive NanoDSL language support to the IntelliJ IDEA plugin, following the same architecture pattern as the existing devins-lang module. The implementation includes a complete language infrastructure with lexer, parser, PSI elements, syntax highlighting, and file type registration for .nanodsl files.
Key Changes:
- New Language Module: Adds
nanodsl-langmodule with full language support infrastructure - Grammar Definitions: Implements lexer and parser grammar files for NanoDSL syntax
- Syntax Highlighting: Provides color scheme mappings for keywords, components, actions, and other language elements
Reviewed changes
Copilot reviewed 70 out of 70 changed files in this pull request and generated 36 comments.
Show a summary per file
| File | Description |
|---|---|
| settings.gradle.kts | Adds nanodsl-lang module to the build |
| build.gradle.kts | Configures nanodsl-lang module with grammar-kit tasks and dependencies |
| cc.unitmesh.nanodsl.language.xml | Registers language extensions for file type, parser, and syntax highlighter |
| NanoDSLLanguage.kt | Defines the NanoDSL language object |
| NanoDSLFileType.kt | Registers .nanodsl file extension |
| NanoDSLFile.kt | Implements PSI file for NanoDSL |
| NanoDSLTokenType.kt | Defines token type for lexer |
| NanoDSLElementType.kt | Defines element type for parser |
| NanoDSLLexerAdapter.kt | Adapts generated JFlex lexer |
| NanoDSLParserDefinition.kt | Configures parser with token sets |
| NanoDSLSyntaxHighlighter.kt | Maps tokens to IDE color schemes |
| NanoDSLSyntaxHighlighterFactory.kt | Factory for syntax highlighter instances |
| NanoDSLLexer.flex | JFlex grammar defining all tokens and keywords |
| NanoDSLParser.bnf | BNF grammar defining language structure |
| Generated PSI files | Auto-generated interfaces and implementations from parser grammar |
| Generated lexer/parser | Auto-generated Java code from grammar files |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| %class NanoDSLLexer | ||
| %class _NanoDSLLexer |
There was a problem hiding this comment.
The lexer class is defined twice with both %class NanoDSLLexer and %class _NanoDSLLexer directives. This will cause the second directive to override the first, resulting in only the _NanoDSLLexer class being generated. Remove the duplicate %class directive on line 16.
| import com.intellij.psi.TokenType; | ||
|
|
||
|
|
||
| class _NanoDSLLexer implements FlexLexer { |
There was a problem hiding this comment.
Unused class: _NanoDSLLexer is not referenced within this codebase. If not used as an external API it should be removed.
| public final int getTokenEnd() { | ||
| return getTokenStart() + yylength(); | ||
| } | ||
|
|
There was a problem hiding this comment.
This method overrides FlexLexer.reset; it is advisable to add an Override annotation.
| @Override |
| public final int getTokenStart() { | ||
| return zzStartRead; | ||
| } | ||
|
|
There was a problem hiding this comment.
This method overrides FlexLexer.getTokenEnd; it is advisable to add an Override annotation.
| @Override |
| int offset = input & 255; | ||
| return offset == input ? ZZ_CMAP_BLOCKS[offset] : ZZ_CMAP_BLOCKS[ZZ_CMAP_TOP[input >> 8] | offset]; | ||
| } | ||
|
|
There was a problem hiding this comment.
This method overrides FlexLexer.getTokenStart; it is advisable to add an Override annotation.
| @Override |
| parseLight(root_, builder_); | ||
| return builder_.getTreeBuilt(); | ||
| } | ||
|
|
There was a problem hiding this comment.
This method overrides LightPsiParser.parseLight; it is advisable to add an Override annotation.
| @Override |
|
|
||
| @SuppressWarnings({"SimplifiableIfStatement", "UnusedAssignment"}) | ||
| public class NanoDSLParser implements PsiParser, LightPsiParser { | ||
|
|
There was a problem hiding this comment.
This method overrides PsiParser.parse; it is advisable to add an Override annotation.
| @Override |
- Add PDF skill for document analysis - Add code review skill with structured criteria - Add test generation skill for unit tests - Add documentation generation skill - Add refactoring skill for code quality improvements - Include comprehensive README with usage instructions
Summary
This PR adds NanoDSL language support for IntelliJ IDEA with syntax highlighting.
Changes
New Module:
nanodsl-langAdded a new language support module at
mpp-idea/mpp-idea-exts/nanodsl-lang/following the same architecture asdevins-lang.Features
NanoDSLLexer.flex): Complete token definitions for NanoDSL syntaxNanoDSLParser.bnf): Grammar rules for NanoDSL language structureNanoDSLSyntaxHighlighter.kt): Color scheme mappingsNanoDSLFileType.kt):.nanodslfile extension registrationSupported Syntax Elements
component,state,request,if,for,in,contentVStack,HStack,Card,SplitView,GenCanvas,Form,ModalText,Image,Badge,Divider,Alert,Progress,Spinner,DataChart,DataTableButton,Input,TextArea,Select,Checkbox,Radio,RadioGroup,Switch,NumberInput,SmartTextField,Slider,DatePicker,DateRangePicker<<(bind read),:=(bind write),+=,-=,*=,/=Navigate,Fetch,ShowToast,StateMutationon_click,on_success,on_error,on_close,on_row_clickGET,POST,PUT,PATCH,DELETEint,float,string,boolColor Scheme
#)Example
Build Verification
Pull Request opened by Augment Code with guidance from the PR author
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.