mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-16 14:23:28 +07:00
[qodana] QD-8459 Wait for initialization of dynamic inspections in qodana
- ProjectInspectionToolRegistrar#dynamicInspectionsFlow: initial value is `null`, when is not `null` it's considered initialized - use `ProjectInspectionToolRegistrar` where it's missing - set category FlexInspect for .inspection.kts, include this category to bundled profiles - introduce Qodana project level inspection registrar since now there is platform one GitOrigin-RevId: 1db1cd6aff95406f7f7dd5558187250a38842492
This commit is contained in:
committed by
intellij-monorepo-bot
parent
9476a90553
commit
afbbf30d42
@@ -5,49 +5,66 @@ import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerEx
|
|||||||
import com.intellij.openapi.components.Service
|
import com.intellij.openapi.components.Service
|
||||||
import com.intellij.openapi.components.service
|
import com.intellij.openapi.components.service
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.launch
|
import org.jetbrains.annotations.ApiStatus
|
||||||
|
|
||||||
@Service(Service.Level.PROJECT)
|
@Service(Service.Level.PROJECT)
|
||||||
class ProjectInspectionToolRegistrar(private val project: Project, scope: CoroutineScope) : InspectionToolsSupplier() {
|
@ApiStatus.Internal
|
||||||
|
class ProjectInspectionToolRegistrar(project: Project, scope: CoroutineScope) : InspectionToolsSupplier() {
|
||||||
companion object {
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
fun getInstance(project: Project): ProjectInspectionToolRegistrar = project.service()
|
fun getInstance(project: Project): ProjectInspectionToolRegistrar = project.service()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val dynamicInspectionsFlow: StateFlow<Set<DynamicInspectionDescriptor>> = dynamicInspectionsFlow(project)
|
private val dynamicInspectionsFlow: StateFlow<Set<DynamicInspectionDescriptor>?> = dynamicInspectionsFlow(project)
|
||||||
.flowOn(Dispatchers.Default)
|
.flowOn(Dispatchers.Default)
|
||||||
.stateIn(scope, SharingStarted.Eagerly, initialValue = emptySet())
|
.stateIn(scope, SharingStarted.Lazily, initialValue = null)
|
||||||
|
|
||||||
|
private val dynamicInspectionsWereInitialized = Job()
|
||||||
|
|
||||||
|
private val updateInspectionProfilesSubscription = scope.launch(Dispatchers.Default, start = CoroutineStart.LAZY) {
|
||||||
|
var oldInspections = emptySet<DynamicInspectionDescriptor>()
|
||||||
|
dynamicInspectionsFlow
|
||||||
|
.filterNotNull()
|
||||||
|
.collect { currentInspections ->
|
||||||
|
try {
|
||||||
|
if (oldInspections == currentInspections) return@collect
|
||||||
|
|
||||||
|
val newInspections = currentInspections - oldInspections
|
||||||
|
val outdatedInspections = oldInspections - currentInspections
|
||||||
|
|
||||||
|
listeners.forEach { listener ->
|
||||||
|
outdatedInspections.forEach {
|
||||||
|
listener.toolRemoved(it.toolWrapper)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
listeners.forEach { listener ->
|
||||||
|
newInspections.forEach {
|
||||||
|
listener.toolAdded(it.toolWrapper)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
oldInspections = currentInspections
|
||||||
|
DaemonCodeAnalyzerEx.getInstance(project).restart()
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
dynamicInspectionsWereInitialized.complete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
InspectionToolRegistrar.getInstance()
|
InspectionToolRegistrar.getInstance()
|
||||||
|
|
||||||
scope.launch(Dispatchers.Default) {
|
|
||||||
var oldInspections = emptySet<DynamicInspectionDescriptor>()
|
|
||||||
dynamicInspectionsFlow.collectLatest { currentInspections ->
|
|
||||||
if (oldInspections == currentInspections) return@collectLatest
|
|
||||||
|
|
||||||
val newInspections = currentInspections - oldInspections
|
|
||||||
val outdatedInspections = oldInspections - currentInspections
|
|
||||||
|
|
||||||
listeners.forEach { listener ->
|
|
||||||
outdatedInspections.forEach {
|
|
||||||
listener.toolRemoved(it.toolWrapper)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
listeners.forEach { listener ->
|
|
||||||
newInspections.forEach {
|
|
||||||
listener.toolAdded(it.toolWrapper)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
oldInspections = currentInspections
|
|
||||||
DaemonCodeAnalyzerEx.getInstance(project).restart()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createTools(): MutableList<InspectionToolWrapper<*, *>> {
|
suspend fun waitForDynamicInspectionsInitialization() {
|
||||||
return (InspectionToolRegistrar.getInstance().createTools() + dynamicInspectionsFlow.value.map { it.toolWrapper }).toMutableList()
|
updateInspectionProfilesSubscription.start()
|
||||||
|
dynamicInspectionsWereInitialized.join()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createTools(): List<InspectionToolWrapper<*, *>> {
|
||||||
|
updateInspectionProfilesSubscription.start()
|
||||||
|
val dynamicTools = dynamicInspectionsFlow.value?.map { it.toolWrapper } ?: emptyList()
|
||||||
|
return InspectionToolRegistrar.getInstance().createTools() + dynamicTools
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,13 +14,16 @@ import kotlinx.coroutines.channels.BufferOverflow
|
|||||||
import kotlinx.coroutines.channels.awaitClose
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
import kotlinx.coroutines.channels.trySendBlocking
|
import kotlinx.coroutines.channels.trySendBlocking
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
|
import org.jetbrains.annotations.ApiStatus
|
||||||
|
|
||||||
private val EP_NAME = ExtensionPointName<DynamicInspectionsProvider>("com.intellij.dynamicInspectionsProvider")
|
private val EP_NAME = ExtensionPointName<DynamicInspectionsProvider>("com.intellij.dynamicInspectionsProvider")
|
||||||
|
|
||||||
|
@ApiStatus.Internal
|
||||||
interface DynamicInspectionsProvider {
|
interface DynamicInspectionsProvider {
|
||||||
fun inspectionsFlow(project: Project): Flow<Set<DynamicInspectionDescriptor>>
|
fun inspections(project: Project): Flow<Set<DynamicInspectionDescriptor>>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiStatus.Internal
|
||||||
sealed class DynamicInspectionDescriptor {
|
sealed class DynamicInspectionDescriptor {
|
||||||
companion object {
|
companion object {
|
||||||
fun fromTool(tool: InspectionProfileEntry): DynamicInspectionDescriptor {
|
fun fromTool(tool: InspectionProfileEntry): DynamicInspectionDescriptor {
|
||||||
@@ -34,30 +37,27 @@ sealed class DynamicInspectionDescriptor {
|
|||||||
|
|
||||||
val toolWrapper: InspectionToolWrapper<*, *> by lazy {
|
val toolWrapper: InspectionToolWrapper<*, *> by lazy {
|
||||||
when(this) {
|
when(this) {
|
||||||
is Global -> object : GlobalInspectionToolWrapper(tool) {
|
is Local -> DynamicLocalInspectionToolWrapper(tool)
|
||||||
override fun createCopy(): GlobalInspectionToolWrapper {
|
is Global -> DynamicGlobalInspectionToolWrapper(tool)
|
||||||
return GlobalInspectionToolWrapper(tool)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is Local -> object : LocalInspectionToolWrapper(tool) {
|
|
||||||
override fun createCopy(): LocalInspectionToolWrapper {
|
|
||||||
return LocalInspectionToolWrapper(tool)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Local(val tool: LocalInspectionTool) : DynamicInspectionDescriptor()
|
||||||
|
|
||||||
class Global(val tool: GlobalInspectionTool) : DynamicInspectionDescriptor()
|
class Global(val tool: GlobalInspectionTool) : DynamicInspectionDescriptor()
|
||||||
|
|
||||||
|
private class DynamicLocalInspectionToolWrapper(tool: LocalInspectionTool) : LocalInspectionToolWrapper(tool) {
|
||||||
|
override fun createCopy(): LocalInspectionToolWrapper = DynamicLocalInspectionToolWrapper(tool)
|
||||||
|
}
|
||||||
|
|
||||||
class Local(val tool: LocalInspectionTool) : DynamicInspectionDescriptor()
|
private class DynamicGlobalInspectionToolWrapper(tool: GlobalInspectionTool) : GlobalInspectionToolWrapper(tool) {
|
||||||
|
override fun createCopy(): GlobalInspectionToolWrapper = DynamicGlobalInspectionToolWrapper(tool)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
internal fun dynamicInspectionsFlow(project: Project): Flow<Set<DynamicInspectionDescriptor>> {
|
internal fun dynamicInspectionsFlow(project: Project): Flow<Set<DynamicInspectionDescriptor>> {
|
||||||
val epUpdatedFlow = callbackFlow {
|
val epUpdatedFlow = callbackFlow {
|
||||||
trySendBlocking(Unit)
|
|
||||||
|
|
||||||
val disposable = Disposer.newDisposable()
|
val disposable = Disposer.newDisposable()
|
||||||
val listener = object : ExtensionPointListener<DynamicInspectionsProvider> {
|
val listener = object : ExtensionPointListener<DynamicInspectionsProvider> {
|
||||||
override fun extensionAdded(extension: DynamicInspectionsProvider, pluginDescriptor: PluginDescriptor) {
|
override fun extensionAdded(extension: DynamicInspectionsProvider, pluginDescriptor: PluginDescriptor) {
|
||||||
@@ -73,14 +73,17 @@ internal fun dynamicInspectionsFlow(project: Project): Flow<Set<DynamicInspectio
|
|||||||
}.buffer(capacity = 1, onBufferOverflow = BufferOverflow.DROP_LATEST)
|
}.buffer(capacity = 1, onBufferOverflow = BufferOverflow.DROP_LATEST)
|
||||||
|
|
||||||
return epUpdatedFlow
|
return epUpdatedFlow
|
||||||
|
.onStart { emit(Unit) }
|
||||||
.flatMapLatest {
|
.flatMapLatest {
|
||||||
val allDynamicCustomInspectionsFlows: List<Flow<Set<DynamicInspectionDescriptor>>> = EP_NAME.extensionList.map { provider ->
|
val allDynamicInspectionsFlows: List<Flow<Set<DynamicInspectionDescriptor>>> = EP_NAME.extensionList.map { provider ->
|
||||||
provider.inspectionsFlow(project)
|
// if returned flow is simply empty, do not block collection of others in combine
|
||||||
.map { inspections -> inspections.toSet() }
|
provider.inspections(project).onEmpty { emit(emptySet()) }
|
||||||
.onStart { emit(emptySet()) }
|
|
||||||
}
|
}
|
||||||
combine(allDynamicCustomInspectionsFlows) {
|
combine(allDynamicInspectionsFlows) {
|
||||||
it.toList().flatten().toSet()
|
it.toList().flatten().toSet()
|
||||||
|
}.onEmpty {
|
||||||
|
// if there are no EP impls, emit the empty set
|
||||||
|
emit(emptySet())
|
||||||
}
|
}
|
||||||
}.distinctUntilChanged()
|
}.distinctUntilChanged()
|
||||||
}
|
}
|
||||||
@@ -29,7 +29,7 @@ public final class InspectionProfileLoadUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static @NotNull InspectionProfileImpl load(@NotNull Path file,
|
public static @NotNull InspectionProfileImpl load(@NotNull Path file,
|
||||||
@NotNull InspectionToolRegistrar registrar,
|
@NotNull InspectionToolsSupplier registrar,
|
||||||
@NotNull InspectionProfileManager profileManager) throws JDOMException, IOException {
|
@NotNull InspectionProfileManager profileManager) throws JDOMException, IOException {
|
||||||
Element element = JDOMUtil.load(file);
|
Element element = JDOMUtil.load(file);
|
||||||
String profileName = getProfileName(file, element);
|
String profileName = getProfileName(file, element);
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ class YamlInspectionProfileImpl private constructor(override val profileName: St
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun loadFrom(project: Project,
|
fun loadFrom(project: Project,
|
||||||
filePath: String = "${getDefaultProfileDirectory(project)}/profile.yaml",
|
filePath: String = "${getDefaultProfileDirectory(project)}/profile.yaml",
|
||||||
toolsSupplier: InspectionToolsSupplier = InspectionToolRegistrar.getInstance(),
|
toolsSupplier: InspectionToolsSupplier = ProjectInspectionToolRegistrar.getInstance(project),
|
||||||
profileManager: BaseInspectionProfileManager = ProjectInspectionProfileManager.getInstance(project)
|
profileManager: BaseInspectionProfileManager = ProjectInspectionProfileManager.getInstance(project)
|
||||||
): YamlInspectionProfileImpl {
|
): YamlInspectionProfileImpl {
|
||||||
val configFile = File(filePath).absoluteFile
|
val configFile = File(filePath).absoluteFile
|
||||||
|
|||||||
@@ -8,9 +8,7 @@ import com.intellij.application.options.schemes.AbstractSchemeActions;
|
|||||||
import com.intellij.application.options.schemes.DescriptionAwareSchemeActions;
|
import com.intellij.application.options.schemes.DescriptionAwareSchemeActions;
|
||||||
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
|
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
|
||||||
import com.intellij.codeInsight.daemon.impl.SeverityRegistrar;
|
import com.intellij.codeInsight.daemon.impl.SeverityRegistrar;
|
||||||
import com.intellij.codeInspection.ex.InspectionProfileImpl;
|
import com.intellij.codeInspection.ex.*;
|
||||||
import com.intellij.codeInspection.ex.InspectionProfileModifiableModel;
|
|
||||||
import com.intellij.codeInspection.ex.InspectionToolRegistrar;
|
|
||||||
import com.intellij.lang.LangBundle;
|
import com.intellij.lang.LangBundle;
|
||||||
import com.intellij.lang.annotation.HighlightSeverity;
|
import com.intellij.lang.annotation.HighlightSeverity;
|
||||||
import com.intellij.openapi.application.ApplicationManager;
|
import com.intellij.openapi.application.ApplicationManager;
|
||||||
@@ -253,8 +251,9 @@ public final class InspectionProfileSchemesPanel extends AbstractDescriptionAwar
|
|||||||
final boolean isProjectLevel = selectedProfile.isProjectLevel() ^ modifyLevel;
|
final boolean isProjectLevel = selectedProfile.isProjectLevel() ^ modifyLevel;
|
||||||
|
|
||||||
BaseInspectionProfileManager profileManager = isProjectLevel ? myProjectProfileManager : myAppProfileManager;
|
BaseInspectionProfileManager profileManager = isProjectLevel ? myProjectProfileManager : myAppProfileManager;
|
||||||
InspectionProfileImpl inspectionProfile =
|
InspectionToolsSupplier inspectionsRegistrar = isProjectLevel ? ProjectInspectionToolRegistrar.getInstance(project)
|
||||||
new InspectionProfileImpl(newName, InspectionToolRegistrar.getInstance(), profileManager);
|
: InspectionToolRegistrar.getInstance();
|
||||||
|
InspectionProfileImpl inspectionProfile = new InspectionProfileImpl(newName, inspectionsRegistrar, profileManager);
|
||||||
|
|
||||||
inspectionProfile.copyFrom(selectedProfile);
|
inspectionProfile.copyFrom(selectedProfile);
|
||||||
inspectionProfile.setName(newName);
|
inspectionProfile.setName(newName);
|
||||||
|
|||||||
Reference in New Issue
Block a user