Complete symbols loaded by imported modules
This commit is contained in:
@@ -50,6 +50,7 @@ internal object JaiSymbolResolver {
|
|||||||
|
|
||||||
private data class ImportBinding(
|
private data class ImportBinding(
|
||||||
val file: PsiFile,
|
val file: PsiFile,
|
||||||
|
val directive: String,
|
||||||
val scope: PsiElement?,
|
val scope: PsiElement?,
|
||||||
val visibleFrom: Int,
|
val visibleFrom: Int,
|
||||||
val boundName: String?,
|
val boundName: String?,
|
||||||
@@ -244,6 +245,7 @@ internal object JaiSymbolResolver {
|
|||||||
|
|
||||||
ImportBinding(
|
ImportBinding(
|
||||||
file = target,
|
file = target,
|
||||||
|
directive = name,
|
||||||
scope = PsiTreeUtil.getParentOfType(literal, JaiBlock::class.java),
|
scope = PsiTreeUtil.getParentOfType(literal, JaiBlock::class.java),
|
||||||
visibleFrom = literal.textOffset,
|
visibleFrom = literal.textOffset,
|
||||||
boundName = boundName,
|
boundName = boundName,
|
||||||
@@ -288,6 +290,7 @@ internal object JaiSymbolResolver {
|
|||||||
val targetPsi = PsiManager.getInstance(file.project).findFile(target) ?: return@mapNotNull null
|
val targetPsi = PsiManager.getInstance(file.project).findFile(target) ?: return@mapNotNull null
|
||||||
ImportBinding(
|
ImportBinding(
|
||||||
file = targetPsi,
|
file = targetPsi,
|
||||||
|
directive = directive,
|
||||||
scope = null,
|
scope = null,
|
||||||
visibleFrom = match.range.first,
|
visibleFrom = match.range.first,
|
||||||
boundName = null,
|
boundName = null,
|
||||||
@@ -331,14 +334,39 @@ internal object JaiSymbolResolver {
|
|||||||
.toSet()
|
.toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun topLevelDeclarations(file: PsiFile): List<JaiDeclName> =
|
/**
|
||||||
PsiTreeUtil
|
* Returns declarations exported by a file, including files it textually loads. A module's
|
||||||
.findChildrenOfType(file, JaiDeclaration::class.java)
|
* public surface is often assembled by loading several sibling files (for example, Basic
|
||||||
.asSequence()
|
* loads Print.jai). Do not follow #import here: an imported module's private dependencies
|
||||||
.flatMap { declaration -> declaration.declNames.declNameList.asSequence() }
|
* must not become unqualified candidates in the importing file.
|
||||||
.filter { PsiTreeUtil.getParentOfType(it, JaiBlock::class.java) == null }
|
*/
|
||||||
.sortedBy { it.textOffset }
|
private fun topLevelDeclarations(file: PsiFile): List<JaiDeclName> = topLevelDeclarations(file, linkedSetOf())
|
||||||
.toList()
|
|
||||||
|
private fun topLevelDeclarations(
|
||||||
|
file: PsiFile,
|
||||||
|
visited: MutableSet<String>,
|
||||||
|
): List<JaiDeclName> {
|
||||||
|
val fileKey = file.virtualFile?.path ?: file.name
|
||||||
|
if (!visited.add(fileKey)) return emptyList()
|
||||||
|
|
||||||
|
val directDeclarations =
|
||||||
|
PsiTreeUtil
|
||||||
|
.findChildrenOfType(file, JaiDeclaration::class.java)
|
||||||
|
.asSequence()
|
||||||
|
.flatMap { declaration -> declaration.declNames.declNameList.asSequence() }
|
||||||
|
.filter { PsiTreeUtil.getParentOfType(it, JaiBlock::class.java) == null }
|
||||||
|
.sortedBy { it.textOffset }
|
||||||
|
.toList()
|
||||||
|
|
||||||
|
val loadedDeclarations =
|
||||||
|
importBindings(file)
|
||||||
|
.asSequence()
|
||||||
|
.filter { it.directive == "#load" }
|
||||||
|
.flatMap { topLevelDeclarations(it.file, visited).asSequence() }
|
||||||
|
.toList()
|
||||||
|
|
||||||
|
return directDeclarations + loadedDeclarations
|
||||||
|
}
|
||||||
|
|
||||||
private fun isVisible(
|
private fun isVisible(
|
||||||
symbol: ScopedSymbol,
|
symbol: ScopedSymbol,
|
||||||
|
|||||||
@@ -111,6 +111,22 @@ class JaiCompletionTest : BasePlatformTestCase() {
|
|||||||
assertTrue("expected alloc in $strings", strings!!.contains("alloc"))
|
assertTrue("expected alloc in $strings", strings!!.contains("alloc"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun testCompletesSymbolsLoadedByImportedModule() {
|
||||||
|
myFixture.configureByText(
|
||||||
|
"nested-module-consumer.jai",
|
||||||
|
"""
|
||||||
|
#import "Basic";
|
||||||
|
main :: () { pri<caret>("hello"); }
|
||||||
|
""".trimIndent(),
|
||||||
|
)
|
||||||
|
|
||||||
|
myFixture.complete(CompletionType.BASIC, 1)
|
||||||
|
assertTrue(
|
||||||
|
"expected print from a file loaded by the imported Basic module: ${myFixture.lookupElementStrings}",
|
||||||
|
myFixture.lookupElementStrings.orEmpty().contains("print"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun testCompletesSymbolsFromLoadedFile() {
|
fun testCompletesSymbolsFromLoadedFile() {
|
||||||
myFixture.addFileToProject("library.jai", "Helper :: () {}")
|
myFixture.addFileToProject("library.jai", "Helper :: () {}")
|
||||||
myFixture.configureByText(
|
myFixture.configureByText(
|
||||||
|
|||||||
Reference in New Issue
Block a user