mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
[qodana] QD-8216 Implement *.inspection.kts inspections
- provide inspections as a `Flow` via `DynamicInspectionsProvider` EP GitOrigin-RevId: 59fdb6af5583f606e060a80319881d15604fd02a
This commit is contained in:
committed by
intellij-monorepo-bot
parent
a4f4693783
commit
58b4d1ba37
@@ -33,6 +33,7 @@
|
||||
</extensionPoint>
|
||||
|
||||
<extensionPoint name="inspectionToolProvider" interface="com.intellij.codeInspection.InspectionToolProvider" dynamic="true"/>
|
||||
<extensionPoint name="dynamicInspectionsProvider" interface="com.intellij.codeInspection.ex.DynamicInspectionsProvider" dynamic="true"/>
|
||||
<extensionPoint name="codeInspection.InspectionExtension" interface="com.intellij.codeInspection.lang.InspectionExtensionsFactory" dynamic="true"/>
|
||||
<extensionPoint name="inspectionsReportConverter" interface="com.intellij.codeInspection.InspectionsReportConverter" dynamic="true"/>
|
||||
|
||||
|
||||
@@ -330,6 +330,10 @@ public abstract class InspectionProfileEntry implements BatchSuppressableTool, O
|
||||
return getShortName(getClass().getSimpleName());
|
||||
}
|
||||
|
||||
public @Nullable String getLanguage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static @NotNull String getShortName(@NotNull String className) {
|
||||
return StringUtil.trimEnd(StringUtil.trimEnd(className, "Inspection"), "InspectionBase");
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ public abstract class InspectionToolWrapper<T extends InspectionProfileEntry, E
|
||||
* @see #isApplicable(Language)
|
||||
*/
|
||||
public @Nullable String getLanguage() {
|
||||
return myEP == null ? null : myEP.language;
|
||||
return myEP == null ? myTool.getLanguage() : myEP.language;
|
||||
}
|
||||
|
||||
public boolean applyToDialects() {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInspection.ex
|
||||
|
||||
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.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Service(Service.Level.PROJECT)
|
||||
class ProjectInspectionToolRegistrar(private val project: Project, scope: CoroutineScope) : InspectionToolsSupplier() {
|
||||
companion object {
|
||||
fun getInstance(project: Project): ProjectInspectionToolRegistrar = project.service()
|
||||
}
|
||||
|
||||
private val dynamicInspectionsFlow: StateFlow<Set<DynamicInspectionDescriptor>> = dynamicInspectionsFlow(project)
|
||||
.flowOn(Dispatchers.Default)
|
||||
.stateIn(scope, SharingStarted.Eagerly, initialValue = emptySet())
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInspection.ex
|
||||
|
||||
import com.intellij.codeInspection.GlobalInspectionTool
|
||||
import com.intellij.codeInspection.InspectionProfileEntry
|
||||
import com.intellij.codeInspection.LocalInspectionTool
|
||||
import com.intellij.openapi.extensions.ExtensionPointListener
|
||||
import com.intellij.openapi.extensions.ExtensionPointName
|
||||
import com.intellij.openapi.extensions.PluginDescriptor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.channels.trySendBlocking
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
private val EP_NAME = ExtensionPointName<DynamicInspectionsProvider>("com.intellij.dynamicInspectionsProvider")
|
||||
|
||||
interface DynamicInspectionsProvider {
|
||||
fun inspectionsFlow(project: Project): Flow<Set<DynamicInspectionDescriptor>>
|
||||
}
|
||||
|
||||
sealed class DynamicInspectionDescriptor {
|
||||
companion object {
|
||||
fun fromTool(tool: InspectionProfileEntry): DynamicInspectionDescriptor {
|
||||
return when(tool) {
|
||||
is LocalInspectionTool -> Local(tool)
|
||||
is GlobalInspectionTool -> Global(tool)
|
||||
else -> error("Got ${tool}, expected ${LocalInspectionTool::class.java} or ${GlobalInspectionTool::class.java}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val toolWrapper: InspectionToolWrapper<*, *> by lazy {
|
||||
when(this) {
|
||||
is Global -> GlobalInspectionToolWrapper(tool)
|
||||
is Local -> LocalInspectionToolWrapper(tool)
|
||||
}
|
||||
}
|
||||
|
||||
class Global(val tool: GlobalInspectionTool) : DynamicInspectionDescriptor()
|
||||
|
||||
|
||||
class Local(val tool: LocalInspectionTool) : DynamicInspectionDescriptor()
|
||||
}
|
||||
|
||||
@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) {
|
||||
trySendBlocking(Unit)
|
||||
}
|
||||
|
||||
override fun extensionRemoved(extension: DynamicInspectionsProvider, pluginDescriptor: PluginDescriptor) {
|
||||
trySendBlocking(Unit)
|
||||
}
|
||||
}
|
||||
EP_NAME.addExtensionPointListener(listener, disposable)
|
||||
awaitClose { Disposer.dispose(disposable) }
|
||||
}.buffer(capacity = 1, onBufferOverflow = BufferOverflow.DROP_LATEST)
|
||||
|
||||
return epUpdatedFlow
|
||||
.flatMapLatest {
|
||||
val allDynamicCustomInspectionsFlows: List<Flow<Set<DynamicInspectionDescriptor>>> = EP_NAME.extensionList.map { provider ->
|
||||
provider.inspectionsFlow(project)
|
||||
.map { inspections -> inspections.toSet() }
|
||||
.onStart { emit(emptySet()) }
|
||||
}
|
||||
combine(allDynamicCustomInspectionsFlows) {
|
||||
it.toList().flatten().toSet()
|
||||
}
|
||||
}.distinctUntilChanged()
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.intellij.profile.codeInspection
|
||||
|
||||
import com.intellij.codeInspection.ex.InspectionProfileImpl
|
||||
import com.intellij.codeInspection.ex.InspectionToolRegistrar
|
||||
import com.intellij.codeInspection.ex.ProjectInspectionToolRegistrar
|
||||
import com.intellij.configurationStore.*
|
||||
import com.intellij.diagnostic.runActivity
|
||||
import com.intellij.openapi.Disposable
|
||||
@@ -59,7 +60,7 @@ open class ProjectInspectionProfileManager(final override val project: Project)
|
||||
name: String,
|
||||
attributeProvider: (String) -> String?,
|
||||
isBundled: Boolean): InspectionProfileImpl {
|
||||
val profile = InspectionProfileImpl(name, InspectionToolRegistrar.getInstance(), this@ProjectInspectionProfileManager, dataHolder)
|
||||
val profile = InspectionProfileImpl(name, ProjectInspectionToolRegistrar.getInstance(project), this@ProjectInspectionProfileManager, dataHolder)
|
||||
profile.isProjectLevel = true
|
||||
return profile
|
||||
}
|
||||
@@ -239,7 +240,7 @@ open class ProjectInspectionProfileManager(final override val project: Project)
|
||||
if (currentScheme == null) {
|
||||
currentScheme = schemeManager.allSchemes.firstOrNull()
|
||||
if (currentScheme == null) {
|
||||
currentScheme = InspectionProfileImpl(PROJECT_DEFAULT_PROFILE_NAME, InspectionToolRegistrar.getInstance(), this)
|
||||
currentScheme = InspectionProfileImpl(PROJECT_DEFAULT_PROFILE_NAME, ProjectInspectionToolRegistrar.getInstance(project), this)
|
||||
currentScheme.copyFrom(InspectionProfileManager.getInstance().currentProfile)
|
||||
currentScheme.isProjectLevel = true
|
||||
currentScheme.name = PROJECT_DEFAULT_PROFILE_NAME
|
||||
|
||||
Reference in New Issue
Block a user