Ide Scopes: Allow including script dependencies into search scope

This includes script dependencies into scopes for some of the IDE services
Should be revisited later to decide if given service should work for scripts
This commit is contained in:
Pavel V. Talanov
2016-06-16 19:24:11 +03:00
parent 7ededbd5e2
commit 1a9c46f34b
9 changed files with 71 additions and 58 deletions
@@ -36,7 +36,7 @@ import org.jetbrains.kotlin.idea.decompiler.classFile.KtClsFile
import org.jetbrains.kotlin.idea.decompiler.navigation.SourceNavigationHelper import org.jetbrains.kotlin.idea.decompiler.navigation.SourceNavigationHelper
import org.jetbrains.kotlin.idea.project.ResolveElementCache import org.jetbrains.kotlin.idea.project.ResolveElementCache
import org.jetbrains.kotlin.idea.stubindex.* import org.jetbrains.kotlin.idea.stubindex.*
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope.sourceAndClassFiles import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope.Companion.sourceAndClassFiles
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils
@@ -108,6 +108,7 @@ private fun PsiElement.getJavaDescriptorResolver(resolutionFacade: ResolutionFac
return resolutionFacade.getFrontendService(this, JavaDescriptorResolver::class.java) return resolutionFacade.getFrontendService(this, JavaDescriptorResolver::class.java)
} }
else { else {
//TODO_R: should this work in scripts?
if (!ProjectRootsUtil.isInProjectOrLibraryClassFile(this)) return null if (!ProjectRootsUtil.isInProjectOrLibraryClassFile(this)) return null
val cacheService = KotlinCacheService.getInstance(project) val cacheService = KotlinCacheService.getInstance(project)
@@ -31,7 +31,7 @@ fun getResolveScope(file: KtFile): GlobalSearchScope {
} }
return when (file.getModuleInfo()) { return when (file.getModuleInfo()) {
is ModuleSourceInfo -> KotlinSourceFilterScope.sourceAndClassFiles(file.resolveScope, file.project) is ModuleSourceInfo -> KotlinSourceFilterScope.projectSourceAndClassFiles(file.resolveScope, file.project)
is ScriptModuleInfo -> file.getModuleInfo().contentScope() is ScriptModuleInfo -> file.getModuleInfo().contentScope()
else -> GlobalSearchScope.EMPTY_SCOPE else -> GlobalSearchScope.EMPTY_SCOPE
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2015 JetBrains s.r.o. * Copyright 2010-2016 JetBrains s.r.o.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -29,67 +29,69 @@ class KotlinSourceFilterScope private constructor(
private val includeProjectSourceFiles: Boolean, private val includeProjectSourceFiles: Boolean,
private val includeLibrarySourceFiles: Boolean, private val includeLibrarySourceFiles: Boolean,
private val includeClassFiles: Boolean, private val includeClassFiles: Boolean,
private val project: Project) : DelegatingGlobalSearchScope(delegate) { private val includeScriptDependencies: Boolean,
private val project: Project
) : DelegatingGlobalSearchScope(delegate) {
private val index = ProjectRootManager.getInstance(project).fileIndex private val index = ProjectRootManager.getInstance(project).fileIndex
//NOTE: avoid recomputing in potentially bottleneck 'contains' method //NOTE: avoid recomputing in potentially bottleneck 'contains' method
private val isJsProjectRef = Ref<Boolean?>(null) private val isJsProjectRef = Ref<Boolean?>(null)
override fun getProject(): Project? { override fun getProject() = project
return project
}
override fun contains(file: VirtualFile): Boolean { override fun contains(file: VirtualFile): Boolean {
if (!super.contains(file)) return false if (!super.contains(file)) return false
return ProjectRootsUtil.isInContent( return ProjectRootsUtil.isInContent(
project, file, includeProjectSourceFiles, includeLibrarySourceFiles, includeClassFiles, index, isJsProjectRef) project, file, includeProjectSourceFiles, includeLibrarySourceFiles, includeClassFiles, includeScriptDependencies, index, isJsProjectRef
)
} }
companion object { companion object {
fun sourcesAndLibraries(delegate: GlobalSearchScope, project: Project): GlobalSearchScope { @JvmStatic
return create(delegate, true, true, true, project) fun sourcesAndLibraries(delegate: GlobalSearchScope, project: Project) = create(delegate, true, true, true, true, project)
}
fun sourceAndClassFiles(delegate: GlobalSearchScope, project: Project): GlobalSearchScope { @JvmStatic
return create(delegate, true, false, true, project) fun sourceAndClassFiles(delegate: GlobalSearchScope, project: Project) = create(delegate, true, false, true, true, project)
}
fun sources(delegate: GlobalSearchScope, project: Project): GlobalSearchScope { @JvmStatic
return create(delegate, true, false, false, project) fun projectSourceAndClassFiles(delegate: GlobalSearchScope, project: Project) = create(delegate, true, false, true, false, project)
}
fun librarySources(delegate: GlobalSearchScope, project: Project): GlobalSearchScope { @JvmStatic
return create(delegate, false, true, false, project) fun sources(delegate: GlobalSearchScope, project: Project) = create(delegate, true, false, false, true, project)
}
fun libraryClassFiles(delegate: GlobalSearchScope, project: Project): GlobalSearchScope { @JvmStatic
return create(delegate, false, false, true, project) fun projectSources(delegate: GlobalSearchScope, project: Project) = create(delegate, true, false, false, false, project)
}
@JvmStatic
fun librarySources(delegate: GlobalSearchScope, project: Project) = create(delegate, false, true, false, true, project)
@JvmStatic
fun libraryClassFiles(delegate: GlobalSearchScope, project: Project) = create(delegate, false, false, true, true, project)
private fun create( private fun create(
delegate: GlobalSearchScope, delegate: GlobalSearchScope,
includeProjectSourceFiles: Boolean, includeProjectSourceFiles: Boolean,
includeLibrarySourceFiles: Boolean, includeLibrarySourceFiles: Boolean,
includeClassFiles: Boolean, includeClassFiles: Boolean,
includeScriptDependencies: Boolean,
project: Project project: Project
): GlobalSearchScope { ): GlobalSearchScope {
if (delegate === GlobalSearchScope.EMPTY_SCOPE) return delegate if (delegate === GlobalSearchScope.EMPTY_SCOPE) return delegate
if (delegate is KotlinSourceFilterScope) { if (delegate is KotlinSourceFilterScope) {
return KotlinSourceFilterScope(
val doIncludeProjectSourceFiles = delegate.includeProjectSourceFiles && includeProjectSourceFiles delegate.myBaseScope,
val doIncludeLibrarySourceFiles = delegate.includeLibrarySourceFiles && includeLibrarySourceFiles includeProjectSourceFiles = delegate.includeProjectSourceFiles && includeProjectSourceFiles,
val doIncludeClassFiles = delegate.includeClassFiles && includeClassFiles includeLibrarySourceFiles = delegate.includeLibrarySourceFiles && includeLibrarySourceFiles,
includeClassFiles = delegate.includeClassFiles && includeClassFiles,
return KotlinSourceFilterScope(delegate.myBaseScope, includeScriptDependencies = delegate.includeScriptDependencies && includeScriptDependencies,
doIncludeProjectSourceFiles, project = project
doIncludeLibrarySourceFiles, )
doIncludeClassFiles,
project)
} }
return KotlinSourceFilterScope(delegate, includeProjectSourceFiles, includeLibrarySourceFiles, includeClassFiles, project) return KotlinSourceFilterScope(delegate, includeProjectSourceFiles, includeLibrarySourceFiles, includeClassFiles, includeScriptDependencies, project)
} }
} }
} }
@@ -24,29 +24,38 @@ import com.intellij.openapi.util.Ref
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiDirectory import com.intellij.psi.PsiDirectory
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.KotlinModuleFileType
import org.jetbrains.kotlin.idea.caches.resolve.JsProjectDetector import org.jetbrains.kotlin.idea.caches.resolve.JsProjectDetector
import org.jetbrains.kotlin.idea.core.script.KotlinScriptConfigurationManager
import org.jetbrains.kotlin.idea.decompiler.builtIns.KotlinBuiltInFileType import org.jetbrains.kotlin.idea.decompiler.builtIns.KotlinBuiltInFileType
import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.idea.util.application.runReadAction
private val classFileLike = listOf(JavaClassFileType.INSTANCE, KotlinBuiltInFileType) private val classFileLike = listOf(JavaClassFileType.INSTANCE, KotlinBuiltInFileType, KotlinModuleFileType.INSTANCE)
object ProjectRootsUtil { object ProjectRootsUtil {
@JvmStatic fun isInContent(project: Project, file: VirtualFile, includeProjectSource: Boolean, @JvmStatic fun isInContent(project: Project, file: VirtualFile, includeProjectSource: Boolean,
includeLibrarySource: Boolean, includeLibraryClasses: Boolean, includeLibrarySource: Boolean, includeLibraryClasses: Boolean,
includeScriptDependencies: Boolean,
fileIndex: ProjectFileIndex = ProjectFileIndex.SERVICE.getInstance(project), fileIndex: ProjectFileIndex = ProjectFileIndex.SERVICE.getInstance(project),
isJsProjectRef: Ref<Boolean?>? = null): Boolean { isJsProjectRef: Ref<Boolean?>? = null): Boolean {
if (includeProjectSource && fileIndex.isInSourceContent(file)) {
return true if (includeProjectSource && fileIndex.isInSourceContent(file)) return true
}
if (!includeLibraryClasses && !includeLibrarySource) return false
// NOTE: the following is a workaround for cases when class files are under library source roots and source files are under class roots // NOTE: the following is a workaround for cases when class files are under library source roots and source files are under class roots
val canContainClassFiles = file.fileType == ArchiveFileType.INSTANCE || file.isDirectory val canContainClassFiles = file.fileType == ArchiveFileType.INSTANCE || file.isDirectory
val isClassFile = file.fileType in classFileLike val isClassFile = file.fileType in classFileLike
if ((includeLibraryClasses && (isClassFile || canContainClassFiles) && fileIndex.isInLibraryClasses(file)) || val scriptConfigurationManager = if (includeScriptDependencies) KotlinScriptConfigurationManager.getInstance(project) else null
(includeLibrarySource && !isClassFile && fileIndex.isInLibrarySource(file))) {
return true if (includeLibraryClasses && (isClassFile || canContainClassFiles)) {
if (fileIndex.isInLibraryClasses(file)) return true
if (scriptConfigurationManager?.getAllScriptsClasspathScope()?.contains(file) == true) return true
}
if (includeLibrarySource && !isClassFile) {
if (fileIndex.isInLibrarySource(file)) return true
if (scriptConfigurationManager?.getAllLibrarySourcesScope()?.contains(file) == true) return true
} }
if ((includeLibraryClasses && fileIndex.isInLibraryClasses(file)) || if ((includeLibraryClasses && fileIndex.isInLibraryClasses(file)) ||
@@ -65,7 +74,8 @@ object ProjectRootsUtil {
element: PsiElement, element: PsiElement,
includeProjectSource: Boolean, includeProjectSource: Boolean,
includeLibrarySource: Boolean, includeLibrarySource: Boolean,
includeLibraryClasses: Boolean includeLibraryClasses: Boolean,
includeScriptDependencies: Boolean
): Boolean { ): Boolean {
return runReadAction { return runReadAction {
val virtualFile = when (element) { val virtualFile = when (element) {
@@ -74,39 +84,39 @@ object ProjectRootsUtil {
} ?: return@runReadAction false } ?: return@runReadAction false
val project = element.project val project = element.project
return@runReadAction isInContent(project, virtualFile, includeProjectSource, includeLibrarySource, includeLibraryClasses) return@runReadAction isInContent(project, virtualFile, includeProjectSource, includeLibrarySource, includeLibraryClasses, includeScriptDependencies)
} }
} }
@JvmStatic fun isInProjectSource(element: PsiElement): Boolean { @JvmStatic fun isInProjectSource(element: PsiElement): Boolean {
return isInContent(element, includeProjectSource = true, includeLibrarySource = false, includeLibraryClasses = false) return isInContent(element, includeProjectSource = true, includeLibrarySource = false, includeLibraryClasses = false, includeScriptDependencies = false)
} }
@JvmStatic fun isProjectSourceFile(project: Project, file: VirtualFile): Boolean { @JvmStatic fun isProjectSourceFile(project: Project, file: VirtualFile): Boolean {
return isInContent(project, file, includeProjectSource = true, includeLibrarySource = false, includeLibraryClasses = false) return isInContent(project, file, includeProjectSource = true, includeLibrarySource = false, includeLibraryClasses = false, includeScriptDependencies = false)
} }
@JvmStatic fun isInProjectOrLibSource(element: PsiElement): Boolean { @JvmStatic fun isInProjectOrLibSource(element: PsiElement): Boolean {
return isInContent(element, includeProjectSource = true, includeLibrarySource = true, includeLibraryClasses = false) return isInContent(element, includeProjectSource = true, includeLibrarySource = true, includeLibraryClasses = false, includeScriptDependencies = false)
} }
@JvmStatic fun isInProjectOrLibraryContent(element: PsiElement): Boolean { @JvmStatic fun isInProjectOrLibraryContent(element: PsiElement): Boolean {
return isInContent(element, includeProjectSource = true, includeLibrarySource = true, includeLibraryClasses = true) return isInContent(element, includeProjectSource = true, includeLibrarySource = true, includeLibraryClasses = true, includeScriptDependencies = true)
} }
@JvmStatic fun isInProjectOrLibraryClassFile(element: PsiElement): Boolean { @JvmStatic fun isInProjectOrLibraryClassFile(element: PsiElement): Boolean {
return isInContent(element, includeProjectSource = true, includeLibrarySource = false, includeLibraryClasses = true) return isInContent(element, includeProjectSource = true, includeLibrarySource = false, includeLibraryClasses = true, includeScriptDependencies = false)
} }
@JvmStatic fun isLibraryClassFile(project: Project, file: VirtualFile): Boolean { @JvmStatic fun isLibraryClassFile(project: Project, file: VirtualFile): Boolean {
return isInContent(project, file, includeProjectSource = false, includeLibrarySource = false, includeLibraryClasses = true) return isInContent(project, file, includeProjectSource = false, includeLibrarySource = false, includeLibraryClasses = true, includeScriptDependencies = true)
} }
@JvmStatic fun isLibrarySourceFile(project: Project, file: VirtualFile): Boolean { @JvmStatic fun isLibrarySourceFile(project: Project, file: VirtualFile): Boolean {
return isInContent(project, file, includeProjectSource = false, includeLibrarySource = true, includeLibraryClasses = false) return isInContent(project, file, includeProjectSource = false, includeLibrarySource = true, includeLibraryClasses = false, includeScriptDependencies = true)
} }
@JvmStatic fun isLibraryFile(project: Project, file: VirtualFile): Boolean { @JvmStatic fun isLibraryFile(project: Project, file: VirtualFile): Boolean {
return isInContent(project, file, includeProjectSource = false, includeLibrarySource = true, includeLibraryClasses = true) return isInContent(project, file, includeProjectSource = false, includeLibrarySource = true, includeLibraryClasses = true, includeScriptDependencies = true)
} }
} }
@@ -47,7 +47,7 @@ class KotlinGotoClassContributor : GotoClassContributor {
override fun getItemsByName(name: String, pattern: String, project: Project, includeNonProjectItems: Boolean): Array<NavigationItem> { override fun getItemsByName(name: String, pattern: String, project: Project, includeNonProjectItems: Boolean): Array<NavigationItem> {
val scope = if (includeNonProjectItems) GlobalSearchScope.allScope(project) else GlobalSearchScope.projectScope(project) val scope = if (includeNonProjectItems) GlobalSearchScope.allScope(project) else GlobalSearchScope.projectScope(project)
val classesOrObjects = KotlinClassShortNameIndex.getInstance().get(name, project, KotlinSourceFilterScope.sourceAndClassFiles(scope, project)) val classesOrObjects = KotlinClassShortNameIndex.getInstance().get(name, project, KotlinSourceFilterScope.projectSourceAndClassFiles(scope, project))
if (classesOrObjects.isEmpty()) return NavigationItem.EMPTY_NAVIGATION_ITEM_ARRAY if (classesOrObjects.isEmpty()) return NavigationItem.EMPTY_NAVIGATION_ITEM_ARRAY
@@ -73,7 +73,7 @@ class KotlinGotoSymbolContributor : ChooseByNameContributor {
override fun getItemsByName(name: String, pattern: String, project: Project, includeNonProjectItems: Boolean): Array<NavigationItem> { override fun getItemsByName(name: String, pattern: String, project: Project, includeNonProjectItems: Boolean): Array<NavigationItem> {
val baseScope = if (includeNonProjectItems) GlobalSearchScope.allScope(project) else GlobalSearchScope.projectScope(project) val baseScope = if (includeNonProjectItems) GlobalSearchScope.allScope(project) else GlobalSearchScope.projectScope(project)
val noLibrarySourceScope = KotlinSourceFilterScope.sourceAndClassFiles(baseScope, project) val noLibrarySourceScope = KotlinSourceFilterScope.projectSourceAndClassFiles(baseScope, project)
val result = ArrayList<NavigationItem>() val result = ArrayList<NavigationItem>()
result += KotlinFunctionShortNameIndex.getInstance().get(name, project, noLibrarySourceScope) result += KotlinFunctionShortNameIndex.getInstance().get(name, project, noLibrarySourceScope)
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2015 JetBrains s.r.o. * Copyright 2010-2016 JetBrains s.r.o.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -193,7 +193,7 @@ class ConflictingExtensionPropertyInspection : AbstractKotlinInspection(), Clean
object : Task.Modal(project, "Searching for imports to delete", true) { object : Task.Modal(project, "Searching for imports to delete", true) {
override fun run(indicator: ProgressIndicator) { override fun run(indicator: ProgressIndicator) {
val importsToDelete = runReadAction { val importsToDelete = runReadAction {
val searchScope = KotlinSourceFilterScope.sources(GlobalSearchScope.projectScope(project), project) val searchScope = KotlinSourceFilterScope.projectSources(GlobalSearchScope.projectScope(project), project)
ReferencesSearch.search(declaration, searchScope) ReferencesSearch.search(declaration, searchScope)
.filterIsInstance<KtSimpleNameReference>() .filterIsInstance<KtSimpleNameReference>()
.mapNotNull { ref -> ref.expression.getStrictParentOfType<KtImportDirective>() } .mapNotNull { ref -> ref.expression.getStrictParentOfType<KtImportDirective>() }
@@ -67,7 +67,7 @@ class DeprecatedSymbolUsageInWholeProjectFix(
object : Task.Modal(project, "Applying '$text'", true) { object : Task.Modal(project, "Applying '$text'", true) {
override fun run(indicator: ProgressIndicator) { override fun run(indicator: ProgressIndicator) {
val usages = runReadAction { val usages = runReadAction {
val searchScope = KotlinSourceFilterScope.sources(GlobalSearchScope.projectScope(project), project) val searchScope = KotlinSourceFilterScope.projectSources(GlobalSearchScope.projectScope(project), project)
ReferencesSearch.search(psiElement, searchScope) ReferencesSearch.search(psiElement, searchScope)
.filterIsInstance<KtSimpleNameReference>() .filterIsInstance<KtSimpleNameReference>()
.map { ref -> ref.expression } .map { ref -> ref.expression }
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2015 JetBrains s.r.o. * Copyright 2010-2016 JetBrains s.r.o.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -44,7 +44,7 @@ open class KotlinDirectInheritorsSearcher() : QueryExecutorBase<PsiClass, Direct
if (scope == null) return if (scope == null) return
runReadAction { runReadAction {
val noLibrarySourceScope = KotlinSourceFilterScope.sourceAndClassFiles(scope, baseClass.project) val noLibrarySourceScope = KotlinSourceFilterScope.projectSourceAndClassFiles(scope, baseClass.project)
KotlinSuperClassIndex.getInstance() KotlinSuperClassIndex.getInstance()
.get(name, baseClass.project, noLibrarySourceScope).asSequence() .get(name, baseClass.project, noLibrarySourceScope).asSequence()
.mapNotNull { candidate -> SourceNavigationHelper.getOriginalPsiClassOrCreateLightClass(candidate) } .mapNotNull { candidate -> SourceNavigationHelper.getOriginalPsiClassOrCreateLightClass(candidate) }