Analysis API: introduce Project Structure
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":core:compiler.common"))
|
||||
implementation(project(":compiler:util"))
|
||||
implementation(project(":compiler:psi"))
|
||||
implementation(intellijCoreDep()) { includeJars("intellij-core", rootProject = rootProject) }
|
||||
}
|
||||
|
||||
kotlin {
|
||||
explicitApi()
|
||||
}
|
||||
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
|
||||
kotlinOptions.freeCompilerArgs += "-opt-in=kotlin.ExperimentalStdlibApi"
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
}
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.analysis.project.structure
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||
import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices
|
||||
import java.nio.file.Path
|
||||
|
||||
/**
|
||||
* Represents a module inside a project.
|
||||
*
|
||||
* [KtModule] is a Source Set (or considering a new project model naming a Fragment).
|
||||
* Some examples of a module: main source set, test source set, library, JDK.
|
||||
*
|
||||
*/
|
||||
public sealed interface KtModule {
|
||||
/**
|
||||
* A list of Regular dependencies. Regular dependency allows current module to see symbols from dependent module.
|
||||
* In a case for a source set, it can be other source set it depends on, library, or SDK.
|
||||
*
|
||||
* The dependencies list is non-transitive and should not include current module.
|
||||
*/
|
||||
public val directRegularDependencies: List<KtModule>
|
||||
|
||||
/**
|
||||
* Only for Kotlin MPP project.
|
||||
* A list of Refinement dependencies.
|
||||
* Refinement dependency express that the current module can provide actual declarations for expect declarations from dependent module,
|
||||
* as well as see internal symbols of dependent module.
|
||||
*
|
||||
* The dependencies list is non-transitive and should not include current module.
|
||||
*/
|
||||
public val directRefinementDependencies: List<KtModule>
|
||||
|
||||
/**
|
||||
* A list of Friend dependencies. Friend dependencies express that current module may see internal symbols of dependent module.
|
||||
*
|
||||
* The dependencies list is non-transitive and should not include current module.
|
||||
*/
|
||||
public val directFriendDependencies: List<KtModule>
|
||||
|
||||
/**
|
||||
* A [GlobalSearchScope] which belongs to a module content.
|
||||
*
|
||||
* Contract: `module.contentScope.contains(file) <=> file belongs to this module`
|
||||
*/
|
||||
public val contentScope: GlobalSearchScope
|
||||
|
||||
/**
|
||||
* A platform (e.g, JVM, JS, Native) current module represents.
|
||||
*
|
||||
* @see [TargetPlatform]
|
||||
*/
|
||||
public val platform: TargetPlatform
|
||||
|
||||
public val analyzerServices: PlatformDependentAnalyzerServices
|
||||
|
||||
/**
|
||||
* [Project] to which module belongs.
|
||||
* If current module depends on some other modules, all those modules should have the same [Project] as the current one.
|
||||
*/
|
||||
public val project: Project?
|
||||
|
||||
/**
|
||||
* Human-readable description of the module. E.g, "main sources of module 'analysis-api'"
|
||||
*/
|
||||
public val moduleDescription: String
|
||||
}
|
||||
|
||||
public sealed interface KtModuleWithProject : KtModule {
|
||||
override val project: Project
|
||||
}
|
||||
|
||||
/**
|
||||
* A module which consists of a set of source declarations inside a projects.
|
||||
*
|
||||
* Generally, a main or test Source Set.
|
||||
*/
|
||||
public interface KtSourceModule : KtModule, KtModuleWithProject {
|
||||
public val moduleName: String
|
||||
|
||||
override val moduleDescription: String
|
||||
get() = "Sources of $moduleName"
|
||||
|
||||
/**
|
||||
* A set of Kotlin settings, like API version, supported features and flags.
|
||||
*/
|
||||
public val languageVersionSettings: LanguageVersionSettings
|
||||
}
|
||||
|
||||
/**
|
||||
* A module which consists of binary declarations.
|
||||
*/
|
||||
public sealed interface KtBinaryModule : KtModule, KtModuleWithProject {
|
||||
/**
|
||||
* A list of binary files which forms a binary module. It can be a list of JARs, KLIBs, folders with .class files.
|
||||
* Should be consistent with [contentScope],
|
||||
* so (pseudo-Kotlin) `
|
||||
* ```
|
||||
* library.contentScope.contains(file) <=> library.getBinaryRoots().listRecursively().contains(file)
|
||||
* ```
|
||||
*/
|
||||
public fun getBinaryRoots(): Collection<Path>
|
||||
}
|
||||
|
||||
/**
|
||||
* A module which represents a binary library. E.g, JAR o KLIB.
|
||||
*/
|
||||
public interface KtLibraryModule : KtBinaryModule {
|
||||
public val libraryName: String
|
||||
|
||||
/**
|
||||
* A library source, if any. If current module is a binary JAR, then [librarySources] corresponds to the sources JAR.
|
||||
*/
|
||||
public val librarySources: KtLibrarySourceModule?
|
||||
|
||||
override val moduleDescription: String
|
||||
get() = "Library $libraryName"
|
||||
}
|
||||
|
||||
/**
|
||||
* A module which represent some SDK. E.g, Java JDK.
|
||||
*/
|
||||
public interface KtSdkModule : KtBinaryModule {
|
||||
public val sdkName: String
|
||||
|
||||
override val moduleDescription: String
|
||||
get() = "SDK $sdkName"
|
||||
}
|
||||
|
||||
/**
|
||||
* A sources for some [KtLibraryModule]
|
||||
*/
|
||||
public interface KtLibrarySourceModule : KtModuleWithProject {
|
||||
public val libraryName: String
|
||||
|
||||
/**
|
||||
* A library binary corresponding to the current library source.
|
||||
* If current module is a source JAR, then [binaryLibrary] is corresponds to the binaries JAR.
|
||||
*/
|
||||
public val binaryLibrary: KtLibraryModule
|
||||
|
||||
override val moduleDescription: String
|
||||
get() = "Library sourced of $libraryName"
|
||||
}
|
||||
|
||||
/**
|
||||
* A set of sources which lives outside project content root. E.g, testdata files or source files of some other project.
|
||||
*/
|
||||
public interface KtNotUnderContentRootModule : KtModule {
|
||||
override val project: Project?
|
||||
get() = null
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.analysis.project.structure
|
||||
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
|
||||
public abstract class KtModuleScopeProvider {
|
||||
/**
|
||||
* Get a scope of binaries on which current source module depends.
|
||||
* Should be equivalent to
|
||||
*
|
||||
* ```
|
||||
* GlobalSearchScope.union(
|
||||
* sourceModule.allDependenciesOfType<KtBinaryModule>()
|
||||
* .map { it.contentScope }
|
||||
* )
|
||||
* ```
|
||||
* For the IDE there can be more optimal implementations.
|
||||
*
|
||||
* See [KtModuleScopeProviderImpl] a correct but non-optimal implementation.
|
||||
*/
|
||||
public abstract fun getModuleLibrariesScope(sourceModule: KtSourceModule): GlobalSearchScope
|
||||
}
|
||||
|
||||
public class KtModuleScopeProviderImpl : KtModuleScopeProvider() {
|
||||
override fun getModuleLibrariesScope(sourceModule: KtSourceModule): GlobalSearchScope {
|
||||
val scopes = sourceModule.allDirectDependenciesOfType<KtBinaryModule>()
|
||||
.map { it.contentScope }
|
||||
.toList()
|
||||
if (scopes.isEmpty()) return GlobalSearchScope.EMPTY_SCOPE
|
||||
return GlobalSearchScope.union(scopes)
|
||||
}
|
||||
}
|
||||
|
||||
public val Project.moduleScopeProvider: KtModuleScopeProvider
|
||||
get() = ServiceManager.getService(this, KtModuleScopeProvider::class.java)
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.analysis.project.structure
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
|
||||
public abstract class ProjectStructureProvider {
|
||||
/**
|
||||
* For a given [PsiElement] get a [KtModule] to which [PsiElement] belongs.
|
||||
*/
|
||||
public abstract fun getKtModuleForKtElement(element: PsiElement): KtModule
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given [PsiElement] get a [KtModule] to which [PsiElement] belongs.
|
||||
* @param project [Project] which contains current [PsiElement]. `PsiElement.project` may be a heavy operation as it includes PSI tree traversal. So, when a [Project] is already available, it is better to pass it explicitly
|
||||
*/
|
||||
public fun PsiElement.getKtModule(project: Project = this.project): KtModule =
|
||||
project.getService(ProjectStructureProvider::class.java)
|
||||
.getKtModuleForKtElement(this)
|
||||
|
||||
/**
|
||||
* For a given [PsiElement] get a [KtModule] to which [PsiElement] belongs.
|
||||
* @return [KtModule] of type [M] if `result <: M`, [java.lang.ClassCastException] otherwise
|
||||
* @param project [Project] which contains current [PsiElement]. `PsiElement.project` may be a heavy operation as it includes PSI tree traversal. So, when a [Project] is already available, it is better to pass it explicitly
|
||||
*/
|
||||
public inline fun <reified M : KtModule> PsiElement.getKtModuleOfType(project: Project = this.project): M =
|
||||
getKtModule(project) as M
|
||||
|
||||
/**
|
||||
* For a given [PsiElement] get a [KtModule] to which [PsiElement] belongs.
|
||||
* @return [KtModule] of type [M] if `result <: M`, `null` otherwise
|
||||
* @param project [Project] which contains current [PsiElement]. `PsiElement.project` may be a heavy operation as it includes PSI tree traversal. So, when a [Project] is already available, it is better to pass it explicitly
|
||||
*/
|
||||
public inline fun <reified M : KtModule> PsiElement.getKtModuleOfTypeSafe(project: Project = this.project): M? =
|
||||
getKtModule(project) as M?
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.analysis.project.structure
|
||||
|
||||
/**
|
||||
* A list of all modules current module can depend onwith regular dependency
|
||||
*
|
||||
* @see KtModule.directRegularDependencies
|
||||
*/
|
||||
public inline fun <reified M : KtModule> KtModule.directRegularDependenciesOfType(): Sequence<M> =
|
||||
directRegularDependencies.asSequence().filterIsInstance<M>()
|
||||
|
||||
/**
|
||||
* A list of all other modules current module can depend on.
|
||||
*
|
||||
* @see KtModule.directRegularDependencies
|
||||
* @see KtModule.directRefinementDependencies
|
||||
* @see KtModule.directFriendDependencies
|
||||
*/
|
||||
public fun KtModule.allDirectDependencies(): Sequence<KtModule> =
|
||||
sequence {
|
||||
yieldAll(directRegularDependencies)
|
||||
yieldAll(directRefinementDependencies)
|
||||
yieldAll(directFriendDependencies)
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of all other modules of type [M] current module can depend on.
|
||||
*
|
||||
* @see KtModule.directRegularDependencies
|
||||
* @see KtModule.directRefinementDependencies
|
||||
* @see KtModule.directFriendDependencies
|
||||
*/
|
||||
public inline fun <reified M : KtModule> KtModule.allDirectDependenciesOfType(): Sequence<M> =
|
||||
allDirectDependencies().filterIsInstance<M>()
|
||||
Reference in New Issue
Block a user