mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-14 18:05:27 +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.project.Project
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
@Service(Service.Level.PROJECT)
|
||||
class ProjectInspectionToolRegistrar(private val project: Project, scope: CoroutineScope) : InspectionToolsSupplier() {
|
||||
@ApiStatus.Internal
|
||||
class ProjectInspectionToolRegistrar(project: Project, scope: CoroutineScope) : InspectionToolsSupplier() {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
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)
|
||||
.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 {
|
||||
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<*, *>> {
|
||||
return (InspectionToolRegistrar.getInstance().createTools() + dynamicInspectionsFlow.value.map { it.toolWrapper }).toMutableList()
|
||||
suspend fun waitForDynamicInspectionsInitialization() {
|
||||
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.trySendBlocking
|
||||
import kotlinx.coroutines.flow.*
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
private val EP_NAME = ExtensionPointName<DynamicInspectionsProvider>("com.intellij.dynamicInspectionsProvider")
|
||||
|
||||
@ApiStatus.Internal
|
||||
interface DynamicInspectionsProvider {
|
||||
fun inspectionsFlow(project: Project): Flow<Set<DynamicInspectionDescriptor>>
|
||||
fun inspections(project: Project): Flow<Set<DynamicInspectionDescriptor>>
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
sealed class DynamicInspectionDescriptor {
|
||||
companion object {
|
||||
fun fromTool(tool: InspectionProfileEntry): DynamicInspectionDescriptor {
|
||||
@@ -34,30 +37,27 @@ sealed class DynamicInspectionDescriptor {
|
||||
|
||||
val toolWrapper: InspectionToolWrapper<*, *> by lazy {
|
||||
when(this) {
|
||||
is Global -> object : GlobalInspectionToolWrapper(tool) {
|
||||
override fun createCopy(): GlobalInspectionToolWrapper {
|
||||
return GlobalInspectionToolWrapper(tool)
|
||||
}
|
||||
}
|
||||
is Local -> object : LocalInspectionToolWrapper(tool) {
|
||||
override fun createCopy(): LocalInspectionToolWrapper {
|
||||
return LocalInspectionToolWrapper(tool)
|
||||
}
|
||||
}
|
||||
is Local -> DynamicLocalInspectionToolWrapper(tool)
|
||||
is Global -> DynamicGlobalInspectionToolWrapper(tool)
|
||||
}
|
||||
}
|
||||
|
||||
class Local(val tool: LocalInspectionTool) : 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)
|
||||
internal fun dynamicInspectionsFlow(project: Project): Flow<Set<DynamicInspectionDescriptor>> {
|
||||
val epUpdatedFlow = callbackFlow {
|
||||
trySendBlocking(Unit)
|
||||
|
||||
val disposable = Disposer.newDisposable()
|
||||
val listener = object : ExtensionPointListener<DynamicInspectionsProvider> {
|
||||
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)
|
||||
|
||||
return epUpdatedFlow
|
||||
.onStart { emit(Unit) }
|
||||
.flatMapLatest {
|
||||
val allDynamicCustomInspectionsFlows: List<Flow<Set<DynamicInspectionDescriptor>>> = EP_NAME.extensionList.map { provider ->
|
||||
provider.inspectionsFlow(project)
|
||||
.map { inspections -> inspections.toSet() }
|
||||
.onStart { emit(emptySet()) }
|
||||
val allDynamicInspectionsFlows: List<Flow<Set<DynamicInspectionDescriptor>>> = EP_NAME.extensionList.map { provider ->
|
||||
// if returned flow is simply empty, do not block collection of others in combine
|
||||
provider.inspections(project).onEmpty { emit(emptySet()) }
|
||||
}
|
||||
combine(allDynamicCustomInspectionsFlows) {
|
||||
combine(allDynamicInspectionsFlows) {
|
||||
it.toList().flatten().toSet()
|
||||
}.onEmpty {
|
||||
// if there are no EP impls, emit the empty set
|
||||
emit(emptySet())
|
||||
}
|
||||
}.distinctUntilChanged()
|
||||
}
|
||||
@@ -29,7 +29,7 @@ public final class InspectionProfileLoadUtil {
|
||||
}
|
||||
|
||||
public static @NotNull InspectionProfileImpl load(@NotNull Path file,
|
||||
@NotNull InspectionToolRegistrar registrar,
|
||||
@NotNull InspectionToolsSupplier registrar,
|
||||
@NotNull InspectionProfileManager profileManager) throws JDOMException, IOException {
|
||||
Element element = JDOMUtil.load(file);
|
||||
String profileName = getProfileName(file, element);
|
||||
|
||||
@@ -116,7 +116,7 @@ class YamlInspectionProfileImpl private constructor(override val profileName: St
|
||||
@JvmStatic
|
||||
fun loadFrom(project: Project,
|
||||
filePath: String = "${getDefaultProfileDirectory(project)}/profile.yaml",
|
||||
toolsSupplier: InspectionToolsSupplier = InspectionToolRegistrar.getInstance(),
|
||||
toolsSupplier: InspectionToolsSupplier = ProjectInspectionToolRegistrar.getInstance(project),
|
||||
profileManager: BaseInspectionProfileManager = ProjectInspectionProfileManager.getInstance(project)
|
||||
): YamlInspectionProfileImpl {
|
||||
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.codeInsight.daemon.impl.HighlightInfoType;
|
||||
import com.intellij.codeInsight.daemon.impl.SeverityRegistrar;
|
||||
import com.intellij.codeInspection.ex.InspectionProfileImpl;
|
||||
import com.intellij.codeInspection.ex.InspectionProfileModifiableModel;
|
||||
import com.intellij.codeInspection.ex.InspectionToolRegistrar;
|
||||
import com.intellij.codeInspection.ex.*;
|
||||
import com.intellij.lang.LangBundle;
|
||||
import com.intellij.lang.annotation.HighlightSeverity;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
@@ -253,8 +251,9 @@ public final class InspectionProfileSchemesPanel extends AbstractDescriptionAwar
|
||||
final boolean isProjectLevel = selectedProfile.isProjectLevel() ^ modifyLevel;
|
||||
|
||||
BaseInspectionProfileManager profileManager = isProjectLevel ? myProjectProfileManager : myAppProfileManager;
|
||||
InspectionProfileImpl inspectionProfile =
|
||||
new InspectionProfileImpl(newName, InspectionToolRegistrar.getInstance(), profileManager);
|
||||
InspectionToolsSupplier inspectionsRegistrar = isProjectLevel ? ProjectInspectionToolRegistrar.getInstance(project)
|
||||
: InspectionToolRegistrar.getInstance();
|
||||
InspectionProfileImpl inspectionProfile = new InspectionProfileImpl(newName, inspectionsRegistrar, profileManager);
|
||||
|
||||
inspectionProfile.copyFrom(selectedProfile);
|
||||
inspectionProfile.setName(newName);
|
||||
|
||||
Reference in New Issue
Block a user