Phase 2: commenter and brace matcher
Brace matching pairs the Jai-specific single-token openers .{ and .[ with the
ordinary } and ]; verified headlessly through the editor highlighter.
This commit is contained in:
52
src/main/kotlin/dev/hgh/jai/editor/JaiEditorSupport.kt
Normal file
52
src/main/kotlin/dev/hgh/jai/editor/JaiEditorSupport.kt
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package dev.hgh.jai.editor
|
||||||
|
|
||||||
|
import com.intellij.lang.BracePair
|
||||||
|
import com.intellij.lang.Commenter
|
||||||
|
import com.intellij.lang.PairedBraceMatcher
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
|
import com.intellij.psi.tree.IElementType
|
||||||
|
import dev.hgh.jai.lexer.JaiTokenTypes
|
||||||
|
|
||||||
|
/** `//` line comments and nesting `/* */` block comments (language reference §2). */
|
||||||
|
class JaiCommenter : Commenter {
|
||||||
|
override fun getLineCommentPrefix(): String = "//"
|
||||||
|
|
||||||
|
override fun getBlockCommentPrefix(): String = "/*"
|
||||||
|
|
||||||
|
override fun getBlockCommentSuffix(): String = "*/"
|
||||||
|
|
||||||
|
// Jai block comments nest, so a commented-out region can be commented again.
|
||||||
|
override fun getCommentedBlockCommentPrefix(): String = "/*"
|
||||||
|
|
||||||
|
override fun getCommentedBlockCommentSuffix(): String = "*/"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Brace matching. Note the two Jai-specific openers: `.{` starts a struct literal and
|
||||||
|
* `.[` starts an array literal (language reference §14.4) — they are single tokens, and
|
||||||
|
* close with an ordinary `}` / `]`.
|
||||||
|
*/
|
||||||
|
class JaiBraceMatcher : PairedBraceMatcher {
|
||||||
|
override fun getPairs(): Array<BracePair> = PAIRS
|
||||||
|
|
||||||
|
override fun isPairedBracesAllowedBeforeType(
|
||||||
|
lbraceType: IElementType,
|
||||||
|
next: IElementType?,
|
||||||
|
): Boolean = true
|
||||||
|
|
||||||
|
override fun getCodeConstructStart(
|
||||||
|
file: PsiFile?,
|
||||||
|
openingBraceOffset: Int,
|
||||||
|
): Int = openingBraceOffset
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val PAIRS =
|
||||||
|
arrayOf(
|
||||||
|
BracePair(JaiTokenTypes.LBRACE, JaiTokenTypes.RBRACE, true),
|
||||||
|
BracePair(JaiTokenTypes.LPAREN, JaiTokenTypes.RPAREN, false),
|
||||||
|
BracePair(JaiTokenTypes.LBRACKET, JaiTokenTypes.RBRACKET, false),
|
||||||
|
BracePair(JaiTokenTypes.BEGIN_STRUCT_LITERAL, JaiTokenTypes.RBRACE, false),
|
||||||
|
BracePair(JaiTokenTypes.BEGIN_ARRAY_LITERAL, JaiTokenTypes.RBRACKET, false),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,6 +26,9 @@
|
|||||||
<lang.syntaxHighlighterFactory
|
<lang.syntaxHighlighterFactory
|
||||||
language="Jai"
|
language="Jai"
|
||||||
implementationClass="dev.hgh.jai.highlighting.JaiSyntaxHighlighterFactory"/>
|
implementationClass="dev.hgh.jai.highlighting.JaiSyntaxHighlighterFactory"/>
|
||||||
|
|
||||||
|
<lang.commenter language="Jai" implementationClass="dev.hgh.jai.editor.JaiCommenter"/>
|
||||||
|
<lang.braceMatcher language="Jai" implementationClass="dev.hgh.jai.editor.JaiBraceMatcher"/>
|
||||||
</extensions>
|
</extensions>
|
||||||
|
|
||||||
</idea-plugin>
|
</idea-plugin>
|
||||||
|
|||||||
60
src/test/kotlin/dev/hgh/jai/editor/JaiEditorSupportTest.kt
Normal file
60
src/test/kotlin/dev/hgh/jai/editor/JaiEditorSupportTest.kt
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package dev.hgh.jai.editor
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.highlighting.BraceMatchingUtil
|
||||||
|
import com.intellij.lang.LanguageBraceMatching
|
||||||
|
import com.intellij.lang.LanguageCommenters
|
||||||
|
import com.intellij.openapi.editor.ex.EditorEx
|
||||||
|
import com.intellij.testFramework.fixtures.BasePlatformTestCase
|
||||||
|
import dev.hgh.jai.JaiFileType
|
||||||
|
import dev.hgh.jai.JaiLanguage
|
||||||
|
|
||||||
|
class JaiEditorSupportTest : BasePlatformTestCase() {
|
||||||
|
fun testCommenterIsRegistered() {
|
||||||
|
val commenter = LanguageCommenters.INSTANCE.forLanguage(JaiLanguage)
|
||||||
|
assertTrue("expected JaiCommenter, got $commenter", commenter is JaiCommenter)
|
||||||
|
assertEquals("//", commenter.lineCommentPrefix)
|
||||||
|
assertEquals("/*", commenter.blockCommentPrefix)
|
||||||
|
assertEquals("*/", commenter.blockCommentSuffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testBraceMatcherIsRegistered() {
|
||||||
|
val matcher = LanguageBraceMatching.INSTANCE.forLanguage(JaiLanguage)
|
||||||
|
assertTrue("expected JaiBraceMatcher, got $matcher", matcher is JaiBraceMatcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testMatchesOrdinaryBraces() {
|
||||||
|
assertMatchForward("main :: () |{ x := 1; }", "main :: () { x := 1; |}")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testMatchesStructLiteralBrace() {
|
||||||
|
// `.{` is one token; it still has to pair with the ordinary closing brace.
|
||||||
|
assertMatchForward("p := Vector2.|{ 1, 2 };", "p := Vector2.{ 1, 2 |};")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testMatchesArrayLiteralBracket() {
|
||||||
|
assertMatchForward("names := .|[ \"a\", \"b\" ];", "names := .[ \"a\", \"b\" |];")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Both arguments are the same text with a `|` marking the caret; the first marks the
|
||||||
|
* opening brace, the second the offset the matcher must jump to.
|
||||||
|
*/
|
||||||
|
private fun assertMatchForward(
|
||||||
|
withOpen: String,
|
||||||
|
withClose: String,
|
||||||
|
) {
|
||||||
|
val open = withOpen.indexOf('|')
|
||||||
|
val close = withClose.indexOf('|')
|
||||||
|
val text = withOpen.replace("|", "")
|
||||||
|
assertEquals("the two markers must describe the same text", text, withClose.replace("|", ""))
|
||||||
|
|
||||||
|
myFixture.configureByText("braces.jai", text)
|
||||||
|
val editor = myFixture.editor as EditorEx
|
||||||
|
val iterator = editor.highlighter.createIterator(open)
|
||||||
|
assertTrue(
|
||||||
|
"no forward match found for the brace at $open in $withOpen",
|
||||||
|
BraceMatchingUtil.matchBrace(editor.document.charsSequence, JaiFileType, iterator, true),
|
||||||
|
)
|
||||||
|
assertEquals(close, iterator.start)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user