internal-tools: move the InvalidCompilationListener to the internal tools

There is no demand for the feature in the Java plugin at the moment. At the same time, we'd like to extend it by some internal capabilities like reporting to Slack, which is more convenient to do when the whole feature is located in a single module.

GitOrigin-RevId: 0249858bb6824a3cab4e416b9ebc2eb83ba295ae
This commit is contained in:
Kirill Likhodedov
2024-02-09 16:03:27 +01:00
committed by intellij-monorepo-bot
parent 8fad92c453
commit ba0d2499cd
5 changed files with 0 additions and 180 deletions

View File

@@ -2501,9 +2501,6 @@
<spellchecker.dictionary.checker implementation="com.intellij.java.frameworks.MavenDictionaryChecker"/>
<statistics.counterUsagesCollector implementationClass="com.intellij.ide.compilation.InvalidCompilationStatistics"/>
<notificationGroup id="Invalid Compilation Errors" displayType="STICKY_BALLOON" hideFromSettings="true" />
<iconMapper mappingFile="JavaUIIconsMapping.json"/>
<optionController implementation="com.intellij.compiler.JavaCompilerConfiguration$Provider"/>
@@ -2550,9 +2547,6 @@
topic="com.intellij.codeInsight.hints.InlayHintsSettings$SettingsListener"/>
<listener class="com.intellij.codeInsight.daemon.problems.pass.ProjectProblemFileRefactoringEventListener"
topic="com.intellij.refactoring.listeners.RefactoringEventListener"/>
<listener class="com.intellij.ide.compilation.InvalidCompilationListener"
topic="com.intellij.openapi.compiler.CompilationStatusListener"
activeInHeadlessMode="false"/>
<listener class="com.intellij.openapi.roots.impl.LanguageLevelChangedListener"
topic="com.intellij.platform.backend.workspace.WorkspaceModelChangeListener"/>
</projectListeners>

View File

@@ -1,25 +0,0 @@
// 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.ide.compilation
import com.intellij.openapi.compiler.CompilationStatusListener
import com.intellij.openapi.compiler.CompileContext
import com.intellij.openapi.compiler.CompilerMessageCategory
import com.intellij.psi.util.ProjectIconsAccessor.isIdeaProject
internal class InvalidCompilationListener : CompilationStatusListener {
override fun compilationFinished(aborted: Boolean, errors: Int, warnings: Int, compileContext: CompileContext) {
val project = compileContext.getProject()
if (!isIdeaProject(project)) {
return
}
val errorMessages = compileContext.getMessages(CompilerMessageCategory.ERROR)
if (errorMessages.isNotEmpty()) {
InvalidCompilationTracker.getInstance(project).analyzeCompilerErrorsInBackground(errorMessages.toList())
}
else if (!aborted) {
InvalidCompilationTracker.getInstance(project).compilationSucceeded()
}
}
}

View File

@@ -1,16 +0,0 @@
// 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.ide.compilation
import com.intellij.internal.statistic.eventLog.EventLogGroup
import com.intellij.internal.statistic.eventLog.events.EventFields
import com.intellij.internal.statistic.service.fus.collectors.CounterUsagesCollector
internal object InvalidCompilationStatistics : CounterUsagesCollector() {
val GROUP: EventLogGroup = EventLogGroup("idea.project.statistics", 2)
val INVALID_COMPILATION_FAILURE = GROUP.registerEvent("invalid.compilation.failure", EventFields.Language)
override fun getGroup(): EventLogGroup {
return GROUP
}
}

View File

@@ -1,128 +0,0 @@
// 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.ide.compilation
import com.intellij.codeInsight.daemon.impl.MainPassesRunner
import com.intellij.java.JavaBundle
import com.intellij.lang.LanguageUtil
import com.intellij.lang.annotation.HighlightSeverity
import com.intellij.notification.Notification
import com.intellij.notification.NotificationAction
import com.intellij.notification.NotificationType
import com.intellij.openapi.application.readAction
import com.intellij.openapi.compiler.CompilerMessage
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.progress.util.ProgressIndicatorBase
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.FileIndexFacade
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.util.NlsSafe
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.profile.codeInspection.InspectionProjectProfileManager
import com.intellij.task.ProjectTaskManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.util.concurrent.atomic.AtomicReference
@Service(Service.Level.PROJECT)
internal class InvalidCompilationTracker(
private val project: Project,
private val scope: CoroutineScope) {
private val notificationRef: AtomicReference<Notification?> = AtomicReference(null)
internal fun analyzeCompilerErrorsInBackground(errorMessages: List<CompilerMessage>) {
scope.launch(Dispatchers.Default) {
val scannedFiles = mutableSetOf<VirtualFile>()
for (message in errorMessages) {
val file = message.getVirtualFile()
if (file != null && scannedFiles.add(file) && isFileGreen(project, file)) {
LOG.warn("Invalid compilation failure in $file")
InvalidCompilationStatistics.INVALID_COMPILATION_FAILURE.log(project, LanguageUtil.getFileLanguage(file))
proposeToRebuildModule(project, file)
break
}
if (scannedFiles.size >= MAX_FILES_TO_SCAN) {
// don't scan too many files to save CPU resources: if there are many errors, and files are also red,
// then probably it is not an invalid compilation case, and manual actions are needed anyway.
break
}
}
}
}
private fun isFileGreen(project: Project, file: VirtualFile): Boolean {
var green = false
ProgressManager.getInstance().executeProcessUnderProgress(Runnable {
val inspectionProfile = InspectionProjectProfileManager.getInstance(project).currentProfile
val errors = MainPassesRunner(project, "", inspectionProfile).runMainPasses(listOf(file), HighlightSeverity.ERROR)
.filterValues { highlightInfos ->
highlightInfos.any { it.severity == HighlightSeverity.ERROR }
}
green = errors.isEmpty()
}, ProgressIndicatorBase())
return green
}
private suspend fun proposeToRebuildModule(project: Project, file: VirtualFile) {
val module = readAction {
FileIndexFacade.getInstance(project).getModuleForFile(file)
}
if (module == null) {
LOG.warn("No module for $file")
return
}
val notification = createNotification(module.name, project)
val previousNotification = notificationRef.getAndSet(notification)
if (previousNotification != null) {
previousNotification.expire()
}
notification.notify(project)
}
private fun createNotification(moduleName: @NlsSafe String, project: Project): Notification {
return Notification(NOTIFICATION_GROUP_ID,
JavaBundle.message("invalid.compilation.notification.title"),
JavaBundle.message("invalid.compilation.notification.content"),
NotificationType.WARNING)
.addAction(NotificationAction.createSimpleExpiring(
JavaBundle.message("invalid.compilation.notification.action.rebuild", moduleName),
Runnable {
val module = ModuleManager.getInstance(project).findModuleByName(moduleName)
if (module != null) {
ProjectTaskManager.getInstance(project).rebuild(module)
}
else {
LOG.warn("Module $moduleName not found")
Messages.showErrorDialog(project,
JavaBundle.message("invalid.compilation.notification.action.rebuild.error.description", moduleName),
JavaBundle.message("invalid.compilation.notification.action.rebuild.error.title", moduleName))
}
}
))
}
fun compilationSucceeded() {
val previousNotification = notificationRef.getAndSet(null)
if (previousNotification != null) {
previousNotification.expire()
}
}
companion object {
private val LOG = logger<InvalidCompilationTracker>()
private const val NOTIFICATION_GROUP_ID = "Invalid Compilation Errors"
private const val MAX_FILES_TO_SCAN = 5
fun getInstance(project: Project): InvalidCompilationTracker {
return project.service()
}
}
}

View File

@@ -1921,11 +1921,6 @@ intention.family.name.add.main.method=Add 'main' method
intention.sequenced.collection.can.be.used.display.name=SequencedCollection method can be used
intention.sequenced.collection.can.be.used.fix.name=Replace with SequencedCollection method call
invalid.compilation.notification.title=Invalid compilation error
invalid.compilation.notification.content=Try to rebuild only the affected module
invalid.compilation.notification.action.rebuild=Rebuild module {0}
invalid.compilation.notification.action.rebuild.error.description=Module {0} not found
invalid.compilation.notification.action.rebuild.error.title=Can''t Rebuild Module {0}
inspection.mapping.before.count.family.name=Mapping call before count()
inspection.mapping.before.count.message=The ''{0}()'' call does not change the final count and might be optimized out.
unknown.library=Unknown Library