Fix Sample reference to resolve cross-module packages correctly

Now @sample links to FqName in IDE will be resolved correctly
 Cause now we resolve packages over all modules in project
 #KT-14710 fixed
This commit is contained in:
Simon Ogorodnik
2017-03-02 17:48:25 +03:00
parent 688802de51
commit 4e98394c38
10 changed files with 114 additions and 9 deletions
@@ -114,7 +114,7 @@ fun createModuleResolverProvider(
)
}
private fun collectAllModuleInfosFromIdeaModel(project: Project): List<IdeaModuleInfo> {
fun collectAllModuleInfosFromIdeaModel(project: Project): List<IdeaModuleInfo> {
val ideaModules = ModuleManager.getInstance(project).modules.toList()
val modulesSourcesInfos = ideaModules.flatMap { listOf(it.productionSourceInfo(), it.testSourceInfo()) }
@@ -19,8 +19,11 @@ package org.jetbrains.kotlin.idea.kdoc
import com.intellij.openapi.project.Project
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.collectAllModuleInfosFromIdeaModel
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.stubindex.KotlinClassShortNameIndex
import org.jetbrains.kotlin.idea.stubindex.KotlinFunctionShortNameIndex
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.BindingContext
@@ -29,16 +32,29 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class IdeSampleResolutionService(val project: Project) : SampleResolutionService {
override fun resolveSample(context: BindingContext, fromDescriptor: DeclarationDescriptor, resolutionFacade: ResolutionFacade, qualifiedName: List<String>): Collection<DeclarationDescriptor> {
val allScope = GlobalSearchScope.projectScope(project)
val shortName = qualifiedName.lastOrNull() ?: return emptyList()
val functions = KotlinFunctionShortNameIndex.getInstance().get(shortName, project, allScope)
val targetFqName = FqName.fromSegments(qualifiedName)
val functions = KotlinFunctionShortNameIndex.getInstance().get(shortName, project, allScope).asSequence()
val classes = KotlinClassShortNameIndex.getInstance().get(shortName, project, allScope).asSequence()
val descriptors = functions.asSequence()
val descriptors = (functions + classes)
.filter { it.fqName == targetFqName }
.map { it.resolveToDescriptor(BodyResolveMode.PARTIAL) } // TODO Filter out not visible due dependencies config descriptors
.toList()
return descriptors
if(descriptors.isNotEmpty())
return descriptors
val packageDescriptors = collectAllModuleInfosFromIdeaModel(project)
.asSequence()
.map { resolutionFacade.findModuleDescriptor(it)?.getPackage(targetFqName) }
.filterNotNull()
.filterNot { it.isEmpty() }
return packageDescriptors.toList()
}
}
@@ -148,7 +148,7 @@ class KDocNameCompletionSession(
val qualifiedLink = kDocLink.getLinkText().split('.').dropLast(1)
val nameFilter = descriptorNameFilter.toNameFilter()
if (qualifiedLink.isNotEmpty()) {
val parentDescriptors = resolveKDocLink(bindingContext, resolutionFacade, declarationDescriptor, null, qualifiedLink)
val parentDescriptors = resolveKDocLink(bindingContext, resolutionFacade, declarationDescriptor, kDocLink.getTagIfSubject(), qualifiedLink)
val childDescriptorsOfPartialLink = parentDescriptors.asSequence().flatMap {
val scope = getKDocLinkResolutionScope(resolutionFacade, it)
collectDescriptorsFromScope(scope, nameFilter, false)
@@ -0,0 +1,19 @@
/**
* @sample samples.SampleGroup.mySample
* @sample samples.megasamples.MegaSamplesGroup.megaSample
* @sample samples.notindir.NotInDirSamples.sssample
* @sample smaplez.a.b.c.Samplez.sssample
*/
fun some<caret>() {
}
//INFO: <pre><b>public</b> <b>fun</b> some(): Unit <i>defined in</i> root package</pre><br/>
//INFO: <dl><dt><b>Samples:</b></dt><dd><a href="psi_element://samples.SampleGroup.mySample"><code>samples.SampleGroup.mySample</code></a><pre><code>
//INFO: println("Hello, world")
//INFO: </code></pre></dd><dd><a href="psi_element://samples.megasamples.MegaSamplesGroup.megaSample"><code>samples.megasamples.MegaSamplesGroup.megaSample</code></a><pre><code>
//INFO: println("...---...")
//INFO: </code></pre></dd><dd><a href="psi_element://samples.notindir.NotInDirSamples.sssample"><code>samples.notindir.NotInDirSamples.sssample</code></a><pre><code>
//INFO: println("location is samplesTest/")
//INFO: </code></pre></dd><dd><a href="psi_element://smaplez.a.b.c.Samplez.sssample"><code>smaplez.a.b.c.Samplez.sssample</code></a><pre><code>// Unresolved</code></pre></dd></dl>
@@ -0,0 +1,7 @@
package samples.notindir
object NotInDirSamples {
fun sssample() {
println("location is samplesTest/")
}
}
@@ -0,0 +1,7 @@
package samples.megasamples
object MegaSamplesGroup {
fun megaSample() {
println("...---...")
}
}
@@ -0,0 +1,7 @@
package samples
object SampleGroup {
fun mySample() {
println("Hello, world")
}
}
@@ -0,0 +1,7 @@
package samplez.a.b.c
object Samplez {
fun sssample() {
println("samplez")
}
}
@@ -21,8 +21,13 @@ import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess
import com.intellij.rt.execution.junit.FileComparisonFailure
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.editor.quickDoc.AbstractQuickDocProviderTest.wrapToFileComparisonFailure
import org.jetbrains.kotlin.idea.stubs.AbstractMultiModuleTest
import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.resolve.BindingContext.DECLARATION_TO_DESCRIPTOR
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.test.KotlinTestUtils
import java.io.File
@@ -44,10 +49,47 @@ class KDocSampleTest : AbstractMultiModuleTest() {
samples.addDependency(code)
doTest("simple/code/usage.kt")
doInfoTest("simple/code/usage.kt")
}
fun doTest(path: String) {
fun testFqName() {
val code = module("code")
val samples = module("samples", hasTestRoot = true)
samples.addDependency(code)
doInfoTest("fqName/code/usage.kt")
doResolveTest("fqName/code/usage.kt", "samples")
doResolveTest("fqName/code/usage.kt", "samples.SampleGroup")
doResolveTest("fqName/code/usage.kt", "samples.megasamples")
doResolveTest("fqName/code/usage.kt", "samples.megasamples.MegaSamplesGroup")
doResolveTest("fqName/code/usage.kt", "samples.notindir")
doResolveTest("fqName/code/usage.kt", "samples.notindir.NotInDirSamples")
doResolveTest("fqName/code/usage.kt", "samplez")
doResolveTest("fqName/code/usage.kt", "samplez.a")
doResolveTest("fqName/code/usage.kt", "samplez.a.b")
doResolveTest("fqName/code/usage.kt", "samplez.a.b.c")
doResolveTest("fqName/code/usage.kt", "samplez.a.b.c.Samplez")
}
fun doResolveTest(path: String, link: String) {
configureByFile(path)
val documentationManager = DocumentationManager.getInstance(myProject)
val targetElement = documentationManager.findTargetElement(myEditor, file)
targetElement as KtElement
val bindingContext = targetElement.analyze()
val descriptor = bindingContext[DECLARATION_TO_DESCRIPTOR, targetElement]!!
val kdoc = descriptor.findKDoc()!! as KDocSection
val resolutionFacade = targetElement.getResolutionFacade()
assertNotEmpty(resolveKDocLink(bindingContext, resolutionFacade, descriptor, kdoc.findTagByName("sample")!!, link.split(".")))
}
fun doInfoTest(path: String) {
val testDataFile = File(testPath, path)
configureByFile(path)
val documentationManager = DocumentationManager.getInstance(myProject)
@@ -77,11 +119,11 @@ class KDocSampleTest : AbstractMultiModuleTest() {
if (expectedInfo.endsWith("...\n")) {
if (!info!!.startsWith(expectedInfo.removeSuffix("...\n"))) {
wrapToFileComparisonFailure(info, testDataPath, textData)
wrapToFileComparisonFailure(info, testDataFile.absolutePath, textData)
}
}
else if (expectedInfo != info) {
wrapToFileComparisonFailure(info, path, textData)
wrapToFileComparisonFailure(info!!, testDataFile.absolutePath, textData)
}
}
}