From 9c3ab2e377644513c1c9c04fa351eae921eeceb4 Mon Sep 17 00:00:00 2001 From: Mark Tkachenko Date: Wed, 4 Aug 2021 18:01:25 +0300 Subject: [PATCH] Problems View refactoring - async highlighters support GitOrigin-RevId: 3283032cd02cc3aa18129c521c79108787d0ecfd --- .../daemon/impl/AsyncDescriptionSupplier.kt | 7 +++ .../toolWindow/HighlightingFileRoot.kt | 13 ++--- .../toolWindow/HighlightingPanel.kt | 6 +-- .../toolWindow/HighlightingProblem.kt | 49 +++++++++++++++---- .../toolWindow/HighlightingWatcher.kt | 12 ++--- .../toolWindow/ProblemsViewState.kt | 2 +- 6 files changed, 62 insertions(+), 27 deletions(-) create mode 100644 platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/AsyncDescriptionSupplier.kt diff --git a/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/AsyncDescriptionSupplier.kt b/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/AsyncDescriptionSupplier.kt new file mode 100644 index 000000000000..44b1af24a1a9 --- /dev/null +++ b/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/AsyncDescriptionSupplier.kt @@ -0,0 +1,7 @@ +package com.intellij.codeInsight.daemon.impl + +import org.jetbrains.concurrency.Promise + +interface AsyncDescriptionSupplier { + fun requestDescription() : Promise +} \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/analysis/problemsView/toolWindow/HighlightingFileRoot.kt b/platform/lang-impl/src/com/intellij/analysis/problemsView/toolWindow/HighlightingFileRoot.kt index 682efd4584a2..eae8fd935bc0 100644 --- a/platform/lang-impl/src/com/intellij/analysis/problemsView/toolWindow/HighlightingFileRoot.kt +++ b/platform/lang-impl/src/com/intellij/analysis/problemsView/toolWindow/HighlightingFileRoot.kt @@ -2,7 +2,6 @@ package com.intellij.analysis.problemsView.toolWindow import com.intellij.analysis.problemsView.Problem -import com.intellij.analysis.problemsView.ProblemsListener import com.intellij.analysis.problemsView.ProblemsProvider import com.intellij.lang.annotation.HighlightSeverity import com.intellij.openapi.editor.ex.RangeHighlighterEx @@ -10,16 +9,16 @@ import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.registry.Registry import com.intellij.openapi.vfs.VirtualFile -open class HighlightingFileRoot(panel: ProblemsViewPanel, val file: VirtualFile) : Root(panel) { +internal class HighlightingFileRoot(panel: ProblemsViewPanel, val file: VirtualFile) : Root(panel) { private val problems = mutableSetOf() private val filter = ProblemFilter(panel.state) - protected val provider = object : ProblemsProvider { + private val provider = object : ProblemsProvider { override val project = panel.project } - protected open val watcher = createWatcher(provider, this, file, HighlightSeverity.INFORMATION.myVal + 1) + private val watcher = createWatcher(provider, file) init { Disposer.register(this, provider) @@ -45,10 +44,8 @@ open class HighlightingFileRoot(panel: ProblemsViewPanel, val file: VirtualFile) else -> emptyList() } - protected open fun createWatcher(provider: ProblemsProvider, - listener: ProblemsListener, - file: VirtualFile, - level: Int): HighlightingWatcher = + private fun createWatcher(provider: ProblemsProvider, + file: VirtualFile): HighlightingWatcher = HighlightingWatcher(provider, this, file, HighlightSeverity.INFORMATION.myVal + 1) override fun getOtherProblemCount() = 0 diff --git a/platform/lang-impl/src/com/intellij/analysis/problemsView/toolWindow/HighlightingPanel.kt b/platform/lang-impl/src/com/intellij/analysis/problemsView/toolWindow/HighlightingPanel.kt index 42757d0e5eab..11e6d03e85a0 100644 --- a/platform/lang-impl/src/com/intellij/analysis/problemsView/toolWindow/HighlightingPanel.kt +++ b/platform/lang-impl/src/com/intellij/analysis/problemsView/toolWindow/HighlightingPanel.kt @@ -22,7 +22,7 @@ import com.intellij.util.ui.tree.TreeUtil import org.jetbrains.annotations.Nls import javax.swing.Icon -open class HighlightingPanel(project: Project, state: ProblemsViewState) +class HighlightingPanel(project: Project, state: ProblemsViewState) : ProblemsViewPanel(project, ID, state, ProblemsViewBundle.messagePointer("problems.view.highlighting")), FileEditorManagerListener, PowerSaveMode.Listener { @@ -88,7 +88,7 @@ open class HighlightingPanel(project: Project, state: ProblemsViewState) currentFile = ClientId.withClientId(myClientId) { findCurrentFile() } } - val currentRoot + internal val currentRoot get() = treeModel.root as? HighlightingFileRoot var currentFile @@ -106,7 +106,7 @@ open class HighlightingPanel(project: Project, state: ProblemsViewState) powerSaveStateChanged() } - protected open fun getRoot(file: VirtualFile): HighlightingFileRoot = HighlightingFileRoot(this, file) + internal fun getRoot(file: VirtualFile): HighlightingFileRoot = HighlightingFileRoot(this, file) fun selectHighlighter(highlighter: RangeHighlighterEx) { val problem = currentRoot?.findProblem(highlighter) ?: return diff --git a/platform/lang-impl/src/com/intellij/analysis/problemsView/toolWindow/HighlightingProblem.kt b/platform/lang-impl/src/com/intellij/analysis/problemsView/toolWindow/HighlightingProblem.kt index 7880c2fa530c..a8825f4ac1e1 100644 --- a/platform/lang-impl/src/com/intellij/analysis/problemsView/toolWindow/HighlightingProblem.kt +++ b/platform/lang-impl/src/com/intellij/analysis/problemsView/toolWindow/HighlightingProblem.kt @@ -1,39 +1,69 @@ // Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.analysis.problemsView.toolWindow +import com.intellij.CommonBundle import com.intellij.analysis.problemsView.FileProblem import com.intellij.analysis.problemsView.ProblemsProvider import com.intellij.codeHighlighting.HighlightDisplayLevel import com.intellij.codeInsight.daemon.HighlightDisplayKey +import com.intellij.codeInsight.daemon.impl.AsyncDescriptionSupplier import com.intellij.codeInsight.daemon.impl.HighlightInfo +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.editor.ex.RangeHighlighterEx import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.vfs.VirtualFile +import com.intellij.ui.AnimatedIcon import com.intellij.xml.util.XmlStringUtil.escapeString +import java.util.concurrent.atomic.AtomicBoolean import javax.swing.Icon -open class HighlightingProblem( +internal class HighlightingProblem( override val provider: ProblemsProvider, override val file: VirtualFile, - private val highlighter: RangeHighlighterEx + val highlighter: RangeHighlighterEx ) : FileProblem { - private fun getIcon(level: HighlightDisplayLevel) = if (severity >= level.severity.myVal) level.icon else null + private fun getIcon(level: HighlightDisplayLevel): Icon? = when { + text.isEmpty() || asyncDescriptionRequested.get() -> AnimatedIcon.Default.INSTANCE + severity >= level.severity.myVal -> level.icon + else -> null + } - open val info: HighlightInfo? + private var asyncDescriptionRequested = AtomicBoolean(false) + private var loading = AtomicBoolean(false) + + val info: HighlightInfo? get() { - return HighlightInfo.fromRangeHighlighter(highlighter) + val info = HighlightInfo.fromRangeHighlighter(highlighter) + if (info is AsyncDescriptionSupplier) { + requestAsyncDescription(info) + } + return info } + private fun requestAsyncDescription(info: AsyncDescriptionSupplier) { + if (!asyncDescriptionRequested.compareAndSet(false, true)) return + loading.set(true) + + info.requestDescription().onSuccess { + // we do that to avoid Concurrent modification exception + ApplicationManager.getApplication().invokeLater { + val panel = ProblemsView.getSelectedPanel(provider.project) as? HighlightingPanel + panel?.currentRoot?.problemUpdated(this) + loading.set(false) + } + } + } + override val icon: Icon get() = HighlightDisplayLevel.find(info?.severity)?.icon - ?: getIcon(HighlightDisplayLevel.ERROR) - ?: getIcon(HighlightDisplayLevel.WARNING) - ?: HighlightDisplayLevel.WEAK_WARNING.icon + ?: getIcon(HighlightDisplayLevel.ERROR) + ?: getIcon(HighlightDisplayLevel.WARNING) + ?: HighlightDisplayLevel.WEAK_WARNING.icon override val text: String get() { - val text = info?.description ?: return "Invalid" + val text = info?.description ?: return CommonBundle.getLoadingTreeNodeText() val pos = text.indexOfFirst { StringUtil.isLineBreak(it) } return if (pos < 0 || text.startsWith("", ignoreCase = true)) text else text.substring(0, pos) + StringUtil.ELLIPSIS @@ -48,6 +78,7 @@ open class HighlightingProblem( override val description: String? get() { val text = info?.description ?: return null + if (text.isEmpty()) return null val pos = text.indexOfFirst { StringUtil.isLineBreak(it) } return if (pos < 0 || text.startsWith("", ignoreCase = true)) null else "" + StringUtil.join(StringUtil.splitByLines(escapeString(text)), "
") diff --git a/platform/lang-impl/src/com/intellij/analysis/problemsView/toolWindow/HighlightingWatcher.kt b/platform/lang-impl/src/com/intellij/analysis/problemsView/toolWindow/HighlightingWatcher.kt index ace172a91f6c..d828c79f36f7 100644 --- a/platform/lang-impl/src/com/intellij/analysis/problemsView/toolWindow/HighlightingWatcher.kt +++ b/platform/lang-impl/src/com/intellij/analysis/problemsView/toolWindow/HighlightingWatcher.kt @@ -14,14 +14,14 @@ import com.intellij.openapi.editor.impl.event.MarkupModelListener import com.intellij.openapi.vfs.VirtualFile import java.lang.ref.WeakReference -open class HighlightingWatcher( - protected val provider: ProblemsProvider, - protected val listener: ProblemsListener, - protected val file: VirtualFile, +internal class HighlightingWatcher( + private val provider: ProblemsProvider, + private val listener: ProblemsListener, + private val file: VirtualFile, private val level: Int) : MarkupModelListener, Disposable { - protected val problems = mutableMapOf() + private val problems = mutableMapOf() private var reference: WeakReference? = null init { @@ -62,7 +62,7 @@ open class HighlightingWatcher( fun findProblem(highlighter: RangeHighlighterEx) = synchronized(problems) { problems[highlighter] } - protected open fun getHighlightingProblem(highlighter: RangeHighlighterEx): HighlightingProblem + private fun getHighlightingProblem(highlighter: RangeHighlighterEx): HighlightingProblem = HighlightingProblem(provider, file, highlighter) private fun getProblem(highlighter: RangeHighlighterEx) = when { diff --git a/platform/lang-impl/src/com/intellij/analysis/problemsView/toolWindow/ProblemsViewState.kt b/platform/lang-impl/src/com/intellij/analysis/problemsView/toolWindow/ProblemsViewState.kt index 60b0de72a619..5cfdfd25e93f 100644 --- a/platform/lang-impl/src/com/intellij/analysis/problemsView/toolWindow/ProblemsViewState.kt +++ b/platform/lang-impl/src/com/intellij/analysis/problemsView/toolWindow/ProblemsViewState.kt @@ -28,7 +28,7 @@ open class ProblemsViewState : BaseState() { var sortByName by property(false) @get:XCollection(style = XCollection.Style.v2) - val hideBySeverity: MutableSet by property(Collections.newSetFromMap(ConcurrentHashMap()), { it.isEmpty() }) + val hideBySeverity: MutableSet by property(Collections.newSetFromMap(ConcurrentHashMap())) { it.isEmpty() } } @State(name = "ProblemsViewState", storages = [(Storage(value = StoragePathMacros.WORKSPACE_FILE))])