diff --git a/src/main/kotlin/org/openpolicyagent/ideaplugin/ide/typing/RegoBraceMatcher.kt b/src/main/kotlin/org/openpolicyagent/ideaplugin/ide/typing/RegoBraceMatcher.kt new file mode 100644 index 0000000..06d7753 --- /dev/null +++ b/src/main/kotlin/org/openpolicyagent/ideaplugin/ide/typing/RegoBraceMatcher.kt @@ -0,0 +1,32 @@ +/* + * Use of this source code is governed by the MIT license that can be + * found in the LICENSE file. + */ + +package org.openpolicyagent.ideaplugin.ide.typing + +import com.intellij.lang.BracePair +import com.intellij.lang.PairedBraceMatcher +import com.intellij.psi.PsiFile +import com.intellij.psi.tree.IElementType +import org.openpolicyagent.ideaplugin.lang.psi.RegoTypes + +/** + * Defines the brace matching for Rego language (ie allow to auto close brace, bracket and parenthesis when typed, also + * color matching braces, brackets...) + */ +class RegoBraceMatcher: PairedBraceMatcher { + override fun getPairs() = PAIRS + + override fun isPairedBracesAllowedBeforeType(lbraceType: IElementType, contextType: IElementType?): Boolean = true + + override fun getCodeConstructStart(file: PsiFile, openingBraceOffset: Int): Int = openingBraceOffset + + companion object { + private val PAIRS: Array<BracePair> = arrayOf( + BracePair(RegoTypes.LBRACE, RegoTypes.RBRACE, true), + BracePair(RegoTypes.LBRACK, RegoTypes.RBRACK, false), + BracePair(RegoTypes.LPAREN, RegoTypes.RPAREN, false) + ) + } +} diff --git a/src/main/resources/META-INF/opa-core.xml b/src/main/resources/META-INF/opa-core.xml index e7b57f0..6990194 100644 --- a/src/main/resources/META-INF/opa-core.xml +++ b/src/main/resources/META-INF/opa-core.xml @@ -33,6 +33,9 @@ <todoIndexer filetype="Rego file" implementationClass="org.openpolicyagent.ideaplugin.ide.todo.RegoLexerBasedTodoIndexer"/> <indexPatternBuilder implementation="org.openpolicyagent.ideaplugin.ide.todo.RegoTodoIndexPatternBuilder"/> + <!-- Typing --> + <lang.braceMatcher language="rego" implementationClass="org.openpolicyagent.ideaplugin.ide.typing.RegoBraceMatcher"/> + <toolWindow id="OPA Console" anchor="bottom" icon="/icons/opa_13_13.svg" diff --git a/src/test/kotlin/org/openpolicyagent/ideaplugin/ide/typing/RegoBraceMatcherTest.kt b/src/test/kotlin/org/openpolicyagent/ideaplugin/ide/typing/RegoBraceMatcherTest.kt new file mode 100644 index 0000000..6b3e3fb --- /dev/null +++ b/src/test/kotlin/org/openpolicyagent/ideaplugin/ide/typing/RegoBraceMatcherTest.kt @@ -0,0 +1,124 @@ +/* + * Use of this source code is governed by the MIT license that can be + * found in the LICENSE file. + */ + +package org.openpolicyagent.ideaplugin.ide.typing + +import com.intellij.codeInsight.highlighting.BraceMatchingUtil +import org.openpolicyagent.ideaplugin.OpaTestBase +import org.openpolicyagent.ideaplugin.lang.RegoFileType + +class RegoBraceMatcherTest: OpaTestBase() { + + fun `test parenthesis are paired`()= doTest( + """ + package main + a := sprinft<caret> + """.trimIndent(), + '(', + """ + package main + a := sprinft(<caret>) + """.trimIndent() + ) + + + fun `test brackets are paired`()= doTest( + """ + package main + a := <caret> + """.trimIndent(), + '[', + """ + package main + a := [<caret>] + """.trimIndent() + ) + + + fun `test braces are paired`()= doTest( + """ + package main + rule_1<caret> + """.trimIndent(), + '{', + """ + package main + rule_1{<caret>} + """.trimIndent() + ) + + fun `test parenthesis deletion`()= doTest( + """ + package main + a := sprinft(<caret>) + """.trimIndent(), + '\b', + """ + package main + a := sprinft<caret> + """.trimIndent() + ) + fun `test brackets deletion`()= doTest( + """ + package main + a := [<caret>] + """.trimIndent(), + '\b', + """ + package main + a := <caret> + """.trimIndent() + ) + + fun `test braces deletion`()= doTest( + """ + package main + rule_1{<caret>} + """.trimIndent(), + '\b', + """ + package main + rule_1<caret> + """.trimIndent() + ) + + fun `test parenthesis match` () = doMatch( + """ + package main + a: = sprintf<caret>("hello %v", name[x]) + """.trimIndent(), + ")" + ) + + fun `test brackets match` () = doMatch( + """ + package main + a: = sprintf("hello %v", name<caret>[x]) + """.trimIndent(), + "]" + ) + + fun `test braces match` () = doMatch( + """ + package main + rule_1 <caret>{ + a: = sprintf("hello %v", name[x]) + } + """.trimIndent(), + "}" + ) + private fun doTest(before: String, type: Char, after: String) { + myFixture.configureByText(RegoFileType, before) + myFixture.type(type) + myFixture.checkResult(after) + } + + private fun doMatch(source: String, coBrace: String) { + myFixture.configureByText(RegoFileType, source) + val expected = source.replace("<caret>", "").lastIndexOf(coBrace) + check(BraceMatchingUtil.getMatchedBraceOffset(myFixture.editor, true, myFixture.file) == expected) + } + +}