Skip to content

Commit

Permalink
Add autocomplete on keyword
Browse files Browse the repository at this point in the history
  • Loading branch information
pkernevez committed Aug 22, 2023
1 parent 22c7928 commit c016195
Show file tree
Hide file tree
Showing 6 changed files with 390 additions and 5 deletions.
19 changes: 17 additions & 2 deletions src/main/kotlin/ch/kleis/lcaplugin/ide/template/ErrorHelper.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,31 @@
package ch.kleis.lcaplugin.ide.template

import ch.kleis.lcaplugin.psi.LcaTypes
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiErrorElement
import com.intellij.psi.PsiFile
import com.intellij.psi.util.elementType

class ErrorHelper {
companion object {
fun isInErrorInRootBlock(element: PsiElement?): Boolean {
val parent = element?.parent
return parent != null &&
parent is PsiErrorElement &&
parent.errorDescription.contains("LcaTokenType.process") &&
parent.errorDescription.contains("LcaTokenType.substance")
containsAllErrors(parent, "process", "substance")
}

fun isInErrorInSubBlock(element: PsiElement?): Boolean {
val parent = element?.parent
return element.elementType == LcaTypes.IDENTIFIER &&
parent != null &&
parent is PsiErrorElement &&
parent.parent != null &&
parent.parent is PsiFile
}

fun containsAllErrors(elt: PsiErrorElement, vararg strings: String): Boolean {
return strings.all { elt.errorDescription.contains("LcaTokenType.${it}") }
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package ch.kleis.lcaplugin.language.ide.syntax

import ch.kleis.lcaplugin.ide.template.ErrorHelper.Companion.containsAllErrors
import ch.kleis.lcaplugin.ide.template.ErrorHelper.Companion.isInErrorInRootBlock
import ch.kleis.lcaplugin.ide.template.ErrorHelper.Companion.isInErrorInSubBlock
import com.intellij.codeInsight.completion.CompletionContributor
import com.intellij.codeInsight.completion.CompletionParameters
import com.intellij.codeInsight.completion.CompletionResultSet
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.psi.PsiErrorElement


private val ALL_MANDATORY_SUB_BLOCK_KEYWORD = listOf(
"name", "type", "compartment", "sub_compartment", "reference_unit", // Substance
"symbol" // Unit
)

class LanguageCompletion : CompletionContributor() {


override fun fillCompletionVariants(parameters: CompletionParameters, result: CompletionResultSet) {
super.fillCompletionVariants(parameters, result)

if (isInErrorInRootBlock(parameters.position)) { // Root Block
result.addElements("package", "import", "process", "variables", "substance", "unit")
} else if (isInErrorInSubBlock(parameters.position)) { // Substance or Unit block
val parent = parameters.position.parent as PsiErrorElement
val expected = findExpectedToken(parent)
if (expected != null && expected in ALL_MANDATORY_SUB_BLOCK_KEYWORD) {
result.addElements(expected)
} else if (containsAllErrors(parent, "impacts", "meta")) { // Substance optional blocks
result.addElements("impacts", "meta")
} else if (containsAllErrors(parent, "Emission", "Resource", "Land_use")) { // Substance.type
result.addElements("Emission", "Resource", "Land_use")
} else if (containsAllErrors(parent, "dimension", "alias_for")) { // Unit exclusive alternatives
result.addElements("dimension", "alias_for")
} else {
// Nothing to complete
}
} else {
// Nothing to complete
}
}

private val expectedPattern = Regex("LcaTokenType\\.(.*) expected, got")

private fun findExpectedToken(elt: PsiErrorElement): String? {
val grp = expectedPattern.find(elt.errorDescription)?.groupValues
return if (grp?.size == 2) grp[1] else null
}

private fun CompletionResultSet.addElements(vararg strings: String) {
strings.forEach { this.addElement(LookupElementBuilder.create(it)) }
}


}
6 changes: 3 additions & 3 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@

<extensions defaultExtensionNs="com.intellij">

<moduleType
id="LCA_MODULE_TYPE"
implementationClass="ch.kleis.lcaplugin.project.LcaModuleType"/>
<completion.contributor
language="LCA"
implementationClass="ch.kleis.lcaplugin.language.ide.syntax.LanguageCompletion"/>

<fileType
name="LCA Definition File"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
package ch.kleis.lcaplugin.language.ide.syntax

import com.intellij.codeInsight.completion.CompletionType
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4

@RunWith(JUnit4::class)
class LanguageCompletionTest : LcaCompletionTestCase() {

@Test
fun lookup_ShouldReturnAllRootKeyword_WithoutPrefix() {
// Given
fixture.configureByFiles("empty_all_keywords.lca")
fixture.complete(CompletionType.BASIC)

// When
val lookupElementStrings = fixture.lookupElementStrings

// Then
assertNotNull(lookupElementStrings)
assertSameElements(lookupElementStrings!!, "import", "package", "process", "substance", "unit", "variables")
}

@Test
fun lookup_ShouldReturnFilterRootKeyword_WithPrefix() {
// Given
fixture.configureByText("empty_only_filtered.lca", "p<caret>")
fixture.complete(CompletionType.BASIC)

// When
val lookupElementStrings = fixture.lookupElementStrings

// Then
assertNotNull(lookupElementStrings)
assertSameElements(lookupElementStrings!!, "import", "package", "process")
}

@Test
fun lookup_ShouldReturnEmpty_ForName() {
// Given
fixture.configureByText(
"sampleShouldReturnEmpty_ForName.lca", """
substance <caret> {
}
"""
)
fixture.complete(CompletionType.BASIC)

// When
val lookupElementStrings = fixture.lookupElementStrings

// Then
assertNotNull(lookupElementStrings)
assertSameElements(lookupElementStrings!!)
}

@Test
fun lookup_ShouldReturnSubstanceKeyWord_ForUnitBlockAndSymbol() {
// Given
fixture.configureByText(
"sampleShouldReturnSubstanceKeyWord_ForSubstanceBlock.lca", """
unit myUnit {
<caret>
}
"""
)
fixture.complete(CompletionType.BASIC)

// When
val lookupElementStrings = fixture.lookupElementStrings

// Then
assertNotNull(lookupElementStrings)
assertSameElements(lookupElementStrings!!, "symbol")
}

@Test
fun lookup_ShouldReturnSubstanceKeyWord_ForUnitBlockAndAliasFor() {
// Given
fixture.configureByText(
"sampleShouldReturnSubstanceKeyWord_ForSubstanceBlock.lca", """
unit myUnit {
symbol = "Symbole"
<caret>
}
"""
)
fixture.complete(CompletionType.BASIC)

// When
val lookupElementStrings = fixture.lookupElementStrings

// Then
assertNotNull(lookupElementStrings)
assertSameElements(lookupElementStrings!!, "dimension", "alias_for")
}

@Test
fun lookup_ShouldReturnSubstanceKeyWord_ForSubstanceBlock() {
// Given
fixture.configureByText(
"sampleShouldReturnSubstanceKeyWord_ForSubstanceBlock.lca", """
substance mySubstance {
<caret>
meta {
}
impacts {
}
}
"""
)
fixture.complete(CompletionType.BASIC)

// When
val lookupElementStrings = fixture.lookupElementStrings

// Then
assertNotNull(lookupElementStrings)
assertSameElements(lookupElementStrings!!, "name")
}

@Test
fun lookup_ShouldReturnEmpty_ForSubstanceBlockAndStringLiteral() {
// Given
fixture.configureByText(
"sampleShouldReturnSubstanceKeyWord_ForSubstanceBlock.lca", """
substance mySubstance {
name = "<caret>"
meta {
}
impacts {
}
}
"""
)
fixture.complete(CompletionType.BASIC)

// When
val lookupElementStrings = fixture.lookupElementStrings

// Then
assertNotNull(lookupElementStrings)
assertSameElements(lookupElementStrings!!)
}

@Test
fun lookup_ShouldReturnSubstanceKeyWord_ForSubstanceBlockAnd2ndKeyword() {
// Given
fixture.configureByText(
"sampleShouldReturnEmpty_ForSubstanceMetaBlock.lca", """
substance mySubstance {
name = "myName"
<caret>
meta {
}
impacts {
}
}
"""
)
fixture.complete(CompletionType.BASIC)

// When
val lookupElementStrings = fixture.lookupElementStrings

// Then
assertNotNull(lookupElementStrings)
assertSameElements(lookupElementStrings!!, "type")
}

@Test
fun lookup_ShouldReturnEmpty_ForSubstanceMetaBlock() {
// Given
fixture.configureByText(
"sampleShouldReturnEmpty_ForSubstanceMetaBlock.lca", """
substance mySubstance {
name = "mySubstanceName"
type = Emission
compartment = "water"
sub_compartment = "sea water"
reference_unit = kg
<caret>
}
"""
)
fixture.complete(CompletionType.BASIC)

// When
val lookupElementStrings = fixture.lookupElementStrings

// Then
assertNotNull(lookupElementStrings)
assertSameElements(lookupElementStrings!!, "meta", "impacts")
}

@Test
fun lookup_ShouldReturnSubstanceType_ForSubstanceType() {
// Given
fixture.configureByText(
"sampleShouldReturnEmpty_ForSubstanceMetaBlock.lca", """
substance mySubstance {
name = "myname"
type =<caret>
compartment = "water"
}
"""
)
fixture.complete(CompletionType.BASIC)

// When
val lookupElementStrings = fixture.lookupElementStrings

// Then
assertNotNull(lookupElementStrings)
assertSameElements(lookupElementStrings!!, "Emission", "Resource", "Land_use")
}

@Test
fun lookup_ShouldReturnEmpty_ForSubstanceImpactBlock() {
// Given
fixture.configureByText(
"sampleShouldReturnEmpty_ForSubstanceImpactBlock.lca", """
substance name {
impacts {
<caret>
}
}
"""
)
fixture.complete(CompletionType.BASIC)

// When
val lookupElementStrings = fixture.lookupElementStrings

// Then
assertNotNull(lookupElementStrings)
assertSameElements(lookupElementStrings!!)
}

}
Loading

0 comments on commit c016195

Please sign in to comment.