diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index dc7d268..a48f3bc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -96,10 +96,10 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x gradlew - - name: Verify plugin binany compatibility + - name: Verify plugin binary compatibility run: ./gradlew :plugin:runPluginVerifier - # This job is successful if all depend jobs are successful. To be able to merge the PR, this job must be successful + # This job is successful if all dependent jobs are successful. To be able to merge the PR, this job must be successful all-checks: needs: [build, check-license, check-gradle-wrapper, verify] runs-on: ubuntu-latest diff --git a/build.gradle.kts b/build.gradle.kts index c3ccb20..e5fd262 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -42,6 +42,11 @@ idea { } } +intellij { + version.set("2022.2.5") + plugins.set(listOf("com.intellij.java")) +} + plugins { idea kotlin("jvm") version "1.8.10" diff --git a/plugin/src/main/resources/META-INF/plugin.xml b/plugin/src/main/resources/META-INF/plugin.xml index 376c7c3..466d2c3 100644 --- a/plugin/src/main/resources/META-INF/plugin.xml +++ b/plugin/src/main/resources/META-INF/plugin.xml @@ -33,6 +33,8 @@ + \ No newline at end of file diff --git a/src/main/grammar/Rego.bnf b/src/main/grammar/Rego.bnf index 0b8434d..5028535 100644 --- a/src/main/grammar/Rego.bnf +++ b/src/main/grammar/Rego.bnf @@ -12,6 +12,7 @@ psiImplClassSuffix="Impl" psiPackage= "org.openpolicyagent.ideaplugin.lang.psi" psiImplPackage="org.openpolicyagent.ideaplugin.lang.psi.impl" + psiImplUtilClass="org.openpolicyagent.ideaplugin.lang.psi.ruleelements.RegoPsiImplUtil" elementTypeHolderClass="org.openpolicyagent.ideaplugin.lang.psi.RegoTypes" elementTypeClass="org.openpolicyagent.ideaplugin.lang.psi.RegoElementType" @@ -67,6 +68,10 @@ module ::= package (import| rule)* package ::= "package" ref import ::= "import" ref ( "as" var )? rule ::= "default"? rule-head rule-body* +{ + mixin="org.openpolicyagent.ideaplugin.lang.psi.ruleelements.RegoRuleElementImpl" + implements="org.openpolicyagent.ideaplugin.lang.psi.ruleelements.RegoRuleElement" +} rule-head ::= var ( "(" rule-args? ")" )? ("[" term "]" )? ( ( ":=" | "=" ) expr )? rule-args ::= term ( "," term )* rule-body ::= else-expr | query-block diff --git a/src/main/kotlin/org/openpolicyagent/ideaplugin/ide/highlight/RegoHighlighterAnnotator.kt b/src/main/kotlin/org/openpolicyagent/ideaplugin/ide/highlight/RegoHighlighterAnnotator.kt index ef1bc0a..52f91c4 100644 --- a/src/main/kotlin/org/openpolicyagent/ideaplugin/ide/highlight/RegoHighlighterAnnotator.kt +++ b/src/main/kotlin/org/openpolicyagent/ideaplugin/ide/highlight/RegoHighlighterAnnotator.kt @@ -6,7 +6,6 @@ package org.openpolicyagent.ideaplugin.ide.highlight import com.intellij.lang.annotation.AnnotationHolder -import com.intellij.lang.annotation.Annotator import com.intellij.lang.annotation.HighlightSeverity import com.intellij.psi.PsiElement import org.openpolicyagent.ideaplugin.ide.colors.RegoColor diff --git a/src/main/kotlin/org/openpolicyagent/ideaplugin/ide/reference/RegoReferenceContributor.kt b/src/main/kotlin/org/openpolicyagent/ideaplugin/ide/reference/RegoReferenceContributor.kt new file mode 100644 index 0000000..7197df7 --- /dev/null +++ b/src/main/kotlin/org/openpolicyagent/ideaplugin/ide/reference/RegoReferenceContributor.kt @@ -0,0 +1,33 @@ +/* + * Use of this source code is governed by the MIT license that can be + * found in the LICENSE file. + */ + +package org.openpolicyagent.ideaplugin.ide.reference + +import com.intellij.openapi.util.TextRange +import com.intellij.patterns.PlatformPatterns +import com.intellij.psi.* +import com.intellij.util.ProcessingContext +import org.openpolicyagent.ideaplugin.lang.psi.RegoReference + +class RegoReferenceContributor : PsiReferenceContributor() { + override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) { + registrar.registerReferenceProvider( + PlatformPatterns.psiElement(PsiLiteralExpression::class.java), + object : PsiReferenceProvider() { + override fun getReferencesByElement( + element: PsiElement, + context: ProcessingContext + ): Array { + val literalExpression = element as PsiLiteralExpression + val value = if (literalExpression.value is String) literalExpression.value as String else null + if (value != null && value.matches(Regex(".+\\{"))) { + val rule = TextRange(0, value.indexOf("{")) + return arrayOf(RegoReference(element, rule)) + } + return PsiReference.EMPTY_ARRAY + } + }) + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/openpolicyagent/ideaplugin/lang/RegoUtil.kt b/src/main/kotlin/org/openpolicyagent/ideaplugin/lang/RegoUtil.kt new file mode 100644 index 0000000..3d80d1c --- /dev/null +++ b/src/main/kotlin/org/openpolicyagent/ideaplugin/lang/RegoUtil.kt @@ -0,0 +1,57 @@ +/* + * Use of this source code is governed by the MIT license that can be + * found in the LICENSE file. + */ + +package org.openpolicyagent.ideaplugin.lang + +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiManager +import com.intellij.psi.search.FileTypeIndex +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.util.PsiTreeUtil +import org.openpolicyagent.ideaplugin.lang.psi.RegoFile +import org.openpolicyagent.ideaplugin.lang.psi.RegoRule + +object RegoUtil { + /** + * Searches the entire project for Rego language files with instances of the Rego-Rule with the given key. + * + * @param project current project + * @param ruleName to check + * @return matching properties + */ + fun findRules(project: Project, ruleName: String): List { + val result = mutableListOf() + val virtualFiles = FileTypeIndex.getFiles(RegoFileType, GlobalSearchScope.allScope(project)) + for (virtualFile in virtualFiles) { + val simpleFile = PsiManager.getInstance(project).findFile(virtualFile!!) as RegoFile? + if (simpleFile != null) { + val rules = PsiTreeUtil.getChildrenOfType(simpleFile, RegoRule::class.java) + if (rules != null) { + for (rule in rules) { + if (ruleName == rule.name) { + result.add(rule) + } + } + } + } + } + return result + } + + fun findRules(project: Project?): List { + val result = mutableListOf() + val virtualFiles = FileTypeIndex.getFiles(RegoFileType, GlobalSearchScope.allScope(project!!)) + for (virtualFile in virtualFiles) { + val regoFile = PsiManager.getInstance(project).findFile(virtualFile!!) as RegoFile? + if (regoFile != null) { + val rules = PsiTreeUtil.getChildrenOfType(regoFile, RegoRule::class.java) + if (rules != null) { + result.addAll(rules) + } + } + } + return result + } +} diff --git a/src/main/kotlin/org/openpolicyagent/ideaplugin/lang/psi/RegoElementFactory.kt b/src/main/kotlin/org/openpolicyagent/ideaplugin/lang/psi/RegoElementFactory.kt new file mode 100644 index 0000000..b08b7ed --- /dev/null +++ b/src/main/kotlin/org/openpolicyagent/ideaplugin/lang/psi/RegoElementFactory.kt @@ -0,0 +1,23 @@ +/* + * Use of this source code is governed by the MIT license that can be + * found in the LICENSE file. + */ + +package org.openpolicyagent.ideaplugin.lang.psi + +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiFileFactory +import org.openpolicyagent.ideaplugin.lang.RegoFileType + + +object RegoElementFactory { + fun createRule(project: Project?, name: String): RegoRule { + val file = createFile(project, name) + return file.firstChild as RegoRule + } + + fun createFile(project: Project?, text: String): RegoFile { + val name = "dummy.rego" + return PsiFileFactory.getInstance(project).createFileFromText(name, RegoFileType, text) as RegoFile + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/openpolicyagent/ideaplugin/lang/psi/RegoReference.kt b/src/main/kotlin/org/openpolicyagent/ideaplugin/lang/psi/RegoReference.kt new file mode 100644 index 0000000..1d7ac49 --- /dev/null +++ b/src/main/kotlin/org/openpolicyagent/ideaplugin/lang/psi/RegoReference.kt @@ -0,0 +1,55 @@ +/* + * Use of this source code is governed by the MIT license that can be + * found in the LICENSE file. + */ + +package org.openpolicyagent.ideaplugin.lang.psi + +import com.intellij.codeInsight.lookup.LookupElement +import com.intellij.codeInsight.lookup.LookupElementBuilder +import com.intellij.openapi.util.TextRange +import com.intellij.psi.* +import org.openpolicyagent.ideaplugin.lang.RegoIcons +import org.openpolicyagent.ideaplugin.lang.RegoUtil + + +class RegoReference(element: PsiElement, textRange: TextRange) : PsiReferenceBase(element, textRange), + PsiPolyVariantReference { + private val key: String + + init { + key = element.text.substring(textRange.startOffset, textRange.endOffset) + } + + override fun multiResolve(incompleteCode: Boolean): Array { + val project = myElement!!.project + val rules = RegoUtil.findRules(project, key) + val results = mutableListOf() + for (rule in rules) { + results.add(PsiElementResolveResult(rule)) + } + return results.toTypedArray() + } + + override fun resolve(): PsiElement? { + val resolveResults = multiResolve(false) + return if (resolveResults.size == 1) resolveResults[0].element else null + } + + override fun getVariants(): Array { + val project = myElement!!.project + val rules = RegoUtil.findRules(project) + val variants = mutableListOf() + for (rule in rules) { + if (rule.name != null && rule.name!!.isNotEmpty()) { + variants.add( + LookupElementBuilder + .create(rule) + .withIcon(RegoIcons.OPA) + .withTypeText(rule.containingFile.name) + ) + } + } + return variants.toTypedArray() + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/openpolicyagent/ideaplugin/lang/psi/ruleelements/RegoPsiImplUtil.kt b/src/main/kotlin/org/openpolicyagent/ideaplugin/lang/psi/ruleelements/RegoPsiImplUtil.kt new file mode 100644 index 0000000..a07ec0b --- /dev/null +++ b/src/main/kotlin/org/openpolicyagent/ideaplugin/lang/psi/ruleelements/RegoPsiImplUtil.kt @@ -0,0 +1,45 @@ +/* + * Use of this source code is governed by the MIT license that can be + * found in the LICENSE file. + */ + +package org.openpolicyagent.ideaplugin.lang.psi.ruleelements + +import com.intellij.psi.PsiElement +import org.openpolicyagent.ideaplugin.lang.psi.RegoElementFactory +import org.openpolicyagent.ideaplugin.lang.psi.RegoRule +import org.openpolicyagent.ideaplugin.lang.psi.RegoTypes + + +class RegoPsiImplUtil { + companion object { + + @JvmStatic + fun getRuleHead(element: RegoRule): String? { + val keyNode = element.node.findChildByType(RegoTypes.RULE_HEAD) + return keyNode?.text + } + + @JvmStatic + fun getName(element: RegoRule): String? { + return getRuleHead(element) + } + + @JvmStatic + fun setName(element: RegoRule, newName: String): PsiElement { + val keyNode = element.node.findChildByType(RegoTypes.RULE_HEAD) + if (keyNode != null) { + val property = RegoElementFactory.createRule(element.project, newName) + val newKeyNode = property.firstChild.node + element.node.replaceChild(keyNode, newKeyNode) + } + return element + } + + @JvmStatic + fun getNameIdentifier(element: RegoRule): PsiElement? { + val keyNode = element.node.findChildByType(RegoTypes.RULE_HEAD) + return keyNode?.psi + } + } +} diff --git a/src/main/kotlin/org/openpolicyagent/ideaplugin/lang/psi/ruleelements/RegoRuleElement.kt b/src/main/kotlin/org/openpolicyagent/ideaplugin/lang/psi/ruleelements/RegoRuleElement.kt new file mode 100644 index 0000000..54d0ac9 --- /dev/null +++ b/src/main/kotlin/org/openpolicyagent/ideaplugin/lang/psi/ruleelements/RegoRuleElement.kt @@ -0,0 +1,10 @@ +/* + * Use of this source code is governed by the MIT license that can be + * found in the LICENSE file. + */ + +package org.openpolicyagent.ideaplugin.lang.psi.ruleelements + +import com.intellij.psi.PsiNameIdentifierOwner + +interface RegoRuleElement : PsiNameIdentifierOwner \ No newline at end of file diff --git a/src/main/kotlin/org/openpolicyagent/ideaplugin/lang/psi/ruleelements/RegoRuleElementImpl.kt b/src/main/kotlin/org/openpolicyagent/ideaplugin/lang/psi/ruleelements/RegoRuleElementImpl.kt new file mode 100644 index 0000000..42c9b9b --- /dev/null +++ b/src/main/kotlin/org/openpolicyagent/ideaplugin/lang/psi/ruleelements/RegoRuleElementImpl.kt @@ -0,0 +1,28 @@ +/* + * Use of this source code is governed by the MIT license that can be + * found in the LICENSE file. + */ + +package org.openpolicyagent.ideaplugin.lang.psi.ruleelements + +import com.intellij.extapi.psi.ASTWrapperPsiElement +import com.intellij.lang.ASTNode +import com.intellij.psi.PsiElement +import org.openpolicyagent.ideaplugin.lang.psi.RegoRule +import org.openpolicyagent.ideaplugin.lang.psi.ruleelements.RegoPsiImplUtil.Companion.getName +import org.openpolicyagent.ideaplugin.lang.psi.ruleelements.RegoPsiImplUtil.Companion.getNameIdentifier +import org.openpolicyagent.ideaplugin.lang.psi.ruleelements.RegoPsiImplUtil.Companion.setName + +abstract class RegoRuleElementImpl(node: ASTNode) : ASTWrapperPsiElement(node), RegoRuleElement { + override fun getName(): String? { + return getName(this as RegoRule) + } + + override fun setName(newName: String): PsiElement { + return setName(this as RegoRule, newName) + } + + override fun getNameIdentifier(): PsiElement? { + return getNameIdentifier(this as RegoRule) + } +} \ No newline at end of file diff --git a/src/main/resources/META-INF/opa-core.xml b/src/main/resources/META-INF/opa-core.xml index 44f6955..8378c03 100644 --- a/src/main/resources/META-INF/opa-core.xml +++ b/src/main/resources/META-INF/opa-core.xml @@ -49,8 +49,7 @@ - - +