package dev.hgh.jai.settings import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.psi.PsiFile import com.intellij.psi.PsiManager import com.intellij.psi.PsiReferenceService import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.psi.util.PsiTreeUtil import com.intellij.testFramework.fixtures.BasePlatformTestCase import dev.hgh.jai.psi.JaiDeclName import dev.hgh.jai.psi.JaiLiteralExpr import dev.hgh.jai.psi.JaiRefExpr import java.nio.file.Files import java.nio.file.Path class JaiConfiguredRootTest : BasePlatformTestCase() { private var temporaryRoot: Path? = null fun testConfiguredRootResolvesImportedModuleAndSymbol() { val module = configureRoot("Custom/module.jai" to "Helper :: () {}") val source = myFixture.addFileToProject( "consumer.jai", """ #import "Custom"; main :: () { Helper(); } """.trimIndent(), ) val moduleLiteral = literalWithText(source, "\"Custom\"") assertEquals( module.path, onlyReference(moduleLiteral) .resolve() ?.containingFile ?.virtualFile ?.path, ) val symbolReference = PsiTreeUtil .findChildrenOfType(source, JaiRefExpr::class.java) .single { it.text == "Helper" } assertEquals( module.path, onlyReference(symbolReference) .resolve() ?.containingFile ?.virtualFile ?.path, ) } fun testConfiguredRootResolvesLoadFileFromConfiguredRoot() { val externalFile = configureRoot("external.jai" to "value :: 1;") val source = myFixture.addFileToProject("consumer.jai", "#load \"external.jai\";") val literal = literalWithText(source, "\"external.jai\"") assertEquals( externalFile.path, onlyReference(literal) .resolve() ?.containingFile ?.virtualFile ?.path, ) } fun testConfiguredRootSuppliesImportAndLoadCompletion() { configureRoot( "Custom/module.jai" to "value :: 1;", "external.jai" to "value :: 1;", ) val configuredRoots = JaiProjectSettings.getInstance(project).configuredRoots() assertEquals("expected one configured VFS root", 1, configuredRoots.size) assertNotNull("expected the configured module directory", configuredRoots.single().findChild("Custom")) myFixture.configureByText("import-consumer.jai", "#import \"Cus\";") myFixture.completeBasic() myFixture.checkResult("#import \"Custom\";") myFixture.configureByText("load-consumer.jai", "#load \"ext\";") myFixture.completeBasic() myFixture.checkResult("#load \"external.jai\";") } fun testReferencesSearchIncludesProjectUsagesForConfiguredExternalDeclaration() { val module = configureRoot("Custom/module.jai" to "Helper :: () {}") val source = myFixture.addFileToProject( "consumer.jai", """ #import "Custom"; main :: () { Helper(); } """.trimIndent(), ) JaiProjectSettings.getInstance(project).refreshProjectRoots() val externalFile = PsiManager .getInstance(project) .findFile(module) assertNotNull("expected PSI for configured external module", externalFile) val declaration = PsiTreeUtil .findChildrenOfType(externalFile!!, JaiDeclName::class.java) .single { it.text == "Helper" } assertTrue( "configured Jai files should be indexed as library sources", com.intellij.openapi.roots.ProjectFileIndex .getInstance(project) .isInLibrarySource(module), ) val usages = ReferencesSearch.search(declaration).findAll() assertEquals( "expected the project usage; scope=${declaration.useScope}, usages=$usages", 1, usages.size, ) assertEquals("Helper", usages.single().element.text) assertEquals( source.virtualFile.path, usages .single() .element.containingFile.virtualFile.path, ) } private fun configureRoot(vararg files: Pair): com.intellij.openapi.vfs.VirtualFile { val root = Files.createTempDirectory("jai-configured-root-").toAbsolutePath().normalize() temporaryRoot = root files.forEach { (relativePath, contents) -> val file = root.resolve(relativePath) Files.createDirectories(file.parent) Files.writeString(file, contents) } val virtualRoot = LocalFileSystem.getInstance().refreshAndFindFileByNioFile(root) assertNotNull("expected the configured root to be visible to VFS", virtualRoot) JaiProjectSettings.getInstance(project).setRootPaths(listOf(root.toString())) return virtualRoot!!.findFileByRelativePath(files.first().first)!! } private fun literalWithText( source: PsiFile, text: String, ): JaiLiteralExpr { val literal = PsiTreeUtil .findChildrenOfType(source, JaiLiteralExpr::class.java) .firstOrNull { it.text == text } assertNotNull("expected string literal $text", literal) return literal!! } private fun onlyReference(element: com.intellij.psi.PsiElement) = PsiReferenceService .getService() .getContributedReferences(element) .also { assertEquals("expected exactly one reference", 1, it.size) } .single() override fun tearDown() { try { super.tearDown() } finally { temporaryRoot?.toFile()?.deleteRecursively() } } }