Phase 5: add completion and symbol navigation
This commit is contained in:
61
src/test/kotlin/dev/hgh/jai/completion/JaiCompletionTest.kt
Normal file
61
src/test/kotlin/dev/hgh/jai/completion/JaiCompletionTest.kt
Normal file
@@ -0,0 +1,61 @@
|
||||
package dev.hgh.jai.completion
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionType
|
||||
import com.intellij.testFramework.fixtures.BasePlatformTestCase
|
||||
|
||||
class JaiCompletionTest : BasePlatformTestCase() {
|
||||
fun testCompletesKeywords() {
|
||||
myFixture.configureByText("keyword.jai", "ret<caret>")
|
||||
|
||||
myFixture.completeBasic()
|
||||
myFixture.checkResult("return")
|
||||
}
|
||||
|
||||
fun testCompletesDirectivesWithHash() {
|
||||
myFixture.configureByText("directive.jai", "#imp<caret>")
|
||||
|
||||
myFixture.completeBasic()
|
||||
myFixture.checkResult("#import")
|
||||
}
|
||||
|
||||
fun testDoesNotCompleteInsideStringLiterals() {
|
||||
myFixture.configureByText("string.jai", "#char \"ret<caret>\";")
|
||||
|
||||
myFixture.complete(CompletionType.BASIC, 1)
|
||||
|
||||
assertTrue("strings should not offer keyword completion", myFixture.lookupElementStrings.isNullOrEmpty())
|
||||
}
|
||||
|
||||
fun testDoesNotOfferModulesForStringImport() {
|
||||
myFixture.configureByText("string-import.jai", "#import,string \"Bas<caret>\";")
|
||||
|
||||
myFixture.complete(CompletionType.BASIC, 1)
|
||||
|
||||
assertTrue("#import,string should not offer file modules", myFixture.lookupElementStrings.isNullOrEmpty())
|
||||
}
|
||||
|
||||
fun testCompletesImportModulesFromLocalJaiModulesDirectory() {
|
||||
myFixture.configureByText("main.jai", "#import \"Bas<caret>\";")
|
||||
|
||||
val strings = completeStrings()
|
||||
|
||||
assertNotNull("module completion should be available", strings)
|
||||
assertTrue("expected Basic in $strings", strings!!.contains("Basic"))
|
||||
assertTrue("expected Base64 in $strings", strings.contains("Base64"))
|
||||
assertFalse("prefix should filter Bucket_Array from $strings", strings.contains("Bucket_Array"))
|
||||
assertFalse("prefix should filter Bit_Array from $strings", strings.contains("Bit_Array"))
|
||||
}
|
||||
|
||||
fun testCompletesLoadFilesRelativeToSource() {
|
||||
myFixture.addFileToProject("other.jai", "value := 1;")
|
||||
myFixture.configureByText("main.jai", "#load \"oth<caret>\";")
|
||||
|
||||
myFixture.completeBasic()
|
||||
myFixture.checkResult("#load \"other.jai\";")
|
||||
}
|
||||
|
||||
private fun completeStrings(): List<String>? {
|
||||
myFixture.complete(CompletionType.BASIC, 1)
|
||||
return myFixture.lookupElementStrings
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package dev.hgh.jai.reference
|
||||
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.PsiReference
|
||||
@@ -9,6 +10,7 @@ import com.intellij.psi.PsiReferenceService
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.testFramework.fixtures.BasePlatformTestCase
|
||||
import dev.hgh.jai.psi.JaiLiteralExpr
|
||||
import dev.hgh.jai.psi.JaiRefExpr
|
||||
|
||||
class JaiReferenceTest : BasePlatformTestCase() {
|
||||
fun testLoadResolvesAFileRelativeToTheSource() {
|
||||
@@ -24,6 +26,117 @@ class JaiReferenceTest : BasePlatformTestCase() {
|
||||
assertEquals(TextRange(1, literal.textLength - 1), reference.rangeInElement)
|
||||
}
|
||||
|
||||
fun testProcedureReferenceResolvesToSameFileDeclaration() {
|
||||
val source =
|
||||
myFixture.addFileToProject(
|
||||
"procedures.jai",
|
||||
"""
|
||||
helper :: () {}
|
||||
main :: () { helper(); }
|
||||
""".trimIndent(),
|
||||
)
|
||||
val referenceElement =
|
||||
PsiTreeUtil
|
||||
.findChildrenOfType(source, JaiRefExpr::class.java)
|
||||
.single { it.text == "helper" }
|
||||
|
||||
val reference = onlyReference(referenceElement)
|
||||
|
||||
assertEquals("helper", reference.resolve()?.text)
|
||||
assertEquals(TextRange(0, referenceElement.textLength), reference.rangeInElement)
|
||||
}
|
||||
|
||||
fun testTypeReferenceResolvesToSameFileDeclaration() {
|
||||
val source =
|
||||
myFixture.addFileToProject(
|
||||
"types.jai",
|
||||
"""
|
||||
Point :: struct { x: int; }
|
||||
use_point :: (point: Point) -> int { return point.x; }
|
||||
""".trimIndent(),
|
||||
)
|
||||
val referenceElement =
|
||||
PsiTreeUtil
|
||||
.findChildrenOfType(source, JaiRefExpr::class.java)
|
||||
.single { it.text == "Point" }
|
||||
|
||||
val reference = onlyReference(referenceElement)
|
||||
|
||||
assertEquals("Point", reference.resolve()?.text)
|
||||
}
|
||||
|
||||
fun testSymbolsResolveThroughLoadedFile() {
|
||||
val target =
|
||||
myFixture.addFileToProject(
|
||||
"library.jai",
|
||||
"""
|
||||
Helper :: () {}
|
||||
Point :: struct { x: int; }
|
||||
""".trimIndent(),
|
||||
)
|
||||
val source =
|
||||
myFixture.addFileToProject(
|
||||
"consumer.jai",
|
||||
"""
|
||||
#load "library.jai";
|
||||
main :: (point: Point) { Helper(); }
|
||||
""".trimIndent(),
|
||||
)
|
||||
val references = PsiTreeUtil.findChildrenOfType(source, JaiRefExpr::class.java)
|
||||
val helperReference = references.single { it.text == "Helper" }
|
||||
val pointReference = references.single { it.text == "Point" }
|
||||
|
||||
assertEquals(
|
||||
target.virtualFile.path,
|
||||
onlyReference(helperReference)
|
||||
.resolve()
|
||||
?.containingFile
|
||||
?.virtualFile
|
||||
?.path,
|
||||
)
|
||||
assertEquals(
|
||||
target.virtualFile.path,
|
||||
onlyReference(pointReference)
|
||||
.resolve()
|
||||
?.containingFile
|
||||
?.virtualFile
|
||||
?.path,
|
||||
)
|
||||
}
|
||||
|
||||
fun testImportedProcedureReferenceResolvesFromJaiModule() {
|
||||
val source =
|
||||
myFixture.addFileToProject(
|
||||
"module-consumer.jai",
|
||||
"""
|
||||
#import "Basic";
|
||||
main :: () { alloc(1); }
|
||||
""".trimIndent(),
|
||||
)
|
||||
val referenceElement =
|
||||
PsiTreeUtil
|
||||
.findChildrenOfType(source, JaiRefExpr::class.java)
|
||||
.single { it.text == "alloc" }
|
||||
|
||||
val resolved = onlyReference(referenceElement).resolve()
|
||||
|
||||
assertEquals("alloc", resolved?.text)
|
||||
assertEquals(
|
||||
"${System.getProperty("user.home")}/.local/jai/modules/Basic/module.jai",
|
||||
resolved?.containingFile?.virtualFile?.path,
|
||||
)
|
||||
}
|
||||
|
||||
fun testUnresolvedSymbolReferenceHasNoTarget() {
|
||||
val source = myFixture.addFileToProject("unresolved.jai", "main :: () { missing(); }")
|
||||
val referenceElement =
|
||||
PsiTreeUtil
|
||||
.findChildrenOfType(source, JaiRefExpr::class.java)
|
||||
.single { it.text == "missing" }
|
||||
|
||||
assertNull(onlyReference(referenceElement).resolve())
|
||||
}
|
||||
|
||||
fun testImportResolvesAJaiModuleDirectory() {
|
||||
val sourceVirtualFile =
|
||||
LocalFileSystem
|
||||
@@ -90,12 +203,12 @@ class JaiReferenceTest : BasePlatformTestCase() {
|
||||
return literal!!
|
||||
}
|
||||
|
||||
private fun onlyReference(literal: JaiLiteralExpr): PsiReference {
|
||||
val references = references(literal)
|
||||
assertEquals("expected exactly one path reference", 1, references.size)
|
||||
private fun onlyReference(element: PsiElement): PsiReference {
|
||||
val references = references(element)
|
||||
assertEquals("expected exactly one reference", 1, references.size)
|
||||
return references.single()
|
||||
}
|
||||
|
||||
private fun references(literal: JaiLiteralExpr): List<PsiReference> =
|
||||
PsiReferenceService.getService().getContributedReferences(literal).toList()
|
||||
private fun references(element: PsiElement): List<PsiReference> =
|
||||
PsiReferenceService.getService().getContributedReferences(element).toList()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user