Skip to content

Commit

Permalink
Implement References - wip (open-policy-agent#113)
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrea Canonica committed Apr 6, 2023
1 parent b64ca67 commit 2db426d
Show file tree
Hide file tree
Showing 13 changed files with 266 additions and 5 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions plugin/src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
<!-- although Rego module type is only created by IDEA, we need it in other IDEs as well
to be able to open a project created in IDEA without errors -->
<moduleType id="REGO_MODULE" implementationClass="org.openpolicyagent.ideaplugin.ide.extensions.RegoModuleType"/>
<psi.referenceContributor language="kotlin"
implementation="org.openpolicyagent.ideaplugin.ide.reference.RegoReferenceContributor"/>
</extensions>

</idea-plugin>
5 changes: 5 additions & 0 deletions src/main/grammar/Rego.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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<out PsiReference> {
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<PsiReference>(RegoReference(element, rule))
}
return PsiReference.EMPTY_ARRAY
}
})
}
}
57 changes: 57 additions & 0 deletions src/main/kotlin/org/openpolicyagent/ideaplugin/lang/RegoUtil.kt
Original file line number Diff line number Diff line change
@@ -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<RegoRule> {
val result = mutableListOf<RegoRule>()
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<RegoRule> {
val result = mutableListOf<RegoRule>()
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
}
}
Original file line number Diff line number Diff line change
@@ -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
}
}
Original file line number Diff line number Diff line change
@@ -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<PsiElement>(element, textRange),
PsiPolyVariantReference {
private val key: String

init {
key = element.text.substring(textRange.startOffset, textRange.endOffset)
}

override fun multiResolve(incompleteCode: Boolean): Array<out ResolveResult> {
val project = myElement!!.project
val rules = RegoUtil.findRules(project, key)
val results = mutableListOf<ResolveResult>()
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<out Any> {
val project = myElement!!.project
val rules = RegoUtil.findRules(project)
val variants = mutableListOf<LookupElement>()
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()
}
}
Original file line number Diff line number Diff line change
@@ -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
}
}
}
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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)
}
}
3 changes: 1 addition & 2 deletions src/main/resources/META-INF/opa-core.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@
<!-- Project configuration -->
<projectService serviceImplementation="org.openpolicyagent.ideaplugin.opa.project.settings.OpaProjectSettings"/>
<projectConfigurable groupId="language" id="Settings.Project.Opa" instance="org.openpolicyagent.ideaplugin.opa.project.settings.OpaOptionsConfigurable"/>

</extensions>
</extensions>

<actions>

Expand Down

0 comments on commit 2db426d

Please sign in to comment.