Skip to content

Commit 6640073

Browse files
committed
feat(lint): add UI for displaying lint inspection errors #288
- Introduced a new `PsiErrorUI` object to handle the display of lint inspection errors in a table format. - Updated `PsiErrorCollector` to return `SketchInspectionError` objects instead of plain strings. - Integrated `PsiErrorUI` into `SingleFileDiffSketch` to
1 parent 62c9526 commit 6640073

File tree

2 files changed

+86
-28
lines changed

2 files changed

+86
-28
lines changed

core/src/main/kotlin/cc/unitmesh/devti/sketch/lint/PsiErrorCollector.kt

Lines changed: 82 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import com.intellij.codeInsight.daemon.impl.DaemonProgressIndicator
77
import com.intellij.codeInspection.InspectionEngine
88
import com.intellij.codeInspection.InspectionManager
99
import com.intellij.codeInspection.ProblemDescriptor
10+
import com.intellij.codeInspection.ProblemHighlightType
1011
import com.intellij.codeInspection.ex.GlobalInspectionContextBase
1112
import com.intellij.codeInspection.ex.LocalInspectionToolWrapper
1213
import com.intellij.codeInspection.util.InspectionMessage
@@ -16,6 +17,8 @@ import com.intellij.openapi.application.runReadAction
1617
import com.intellij.openapi.editor.Document
1718
import com.intellij.openapi.fileEditor.FileDocumentManager
1819
import com.intellij.openapi.project.Project
20+
import com.intellij.openapi.ui.popup.JBPopup
21+
import com.intellij.openapi.ui.popup.JBPopupFactory
1922
import com.intellij.openapi.util.Disposer
2023
import com.intellij.openapi.util.TextRange
2124
import com.intellij.openapi.vfs.VirtualFile
@@ -24,13 +27,85 @@ import com.intellij.psi.PsiElement
2427
import com.intellij.psi.PsiErrorElement
2528
import com.intellij.psi.PsiFile
2629
import com.intellij.psi.PsiManager
30+
import com.intellij.ui.components.JBLabel
31+
import com.intellij.ui.components.JBScrollPane
32+
import com.intellij.ui.table.JBTable
2733
import com.intellij.util.PairProcessor
2834
import com.intellij.util.messages.MessageBusConnection
35+
import java.awt.Dimension
36+
import java.awt.event.MouseAdapter
37+
import java.awt.event.MouseEvent
2938
import java.util.concurrent.CompletableFuture
3039
import java.util.concurrent.TimeUnit
40+
import javax.swing.BorderFactory
41+
import javax.swing.JPanel
42+
import javax.swing.JTable
43+
import javax.swing.table.DefaultTableModel
44+
45+
data class SketchInspectionError(
46+
val lineNumber: Int,
47+
val description: String,
48+
val highlightType: ProblemHighlightType,
49+
) {
50+
companion object {
51+
fun from(problemDescriptor: ProblemDescriptor): SketchInspectionError {
52+
return SketchInspectionError(
53+
problemDescriptor.lineNumber,
54+
problemDescriptor.descriptionTemplate,
55+
problemDescriptor.highlightType
56+
)
57+
}
58+
}
59+
}
60+
61+
object PsiErrorUI {
62+
fun showErrors(errors: List<SketchInspectionError>, panel: JPanel) {
63+
val columnNames = arrayOf("Line", "Description", "Highlight Type")
64+
val data = errors.map {
65+
arrayOf(it.lineNumber, it.description, it.highlightType.toString())
66+
}.toTypedArray()
67+
68+
val tableModel = DefaultTableModel(data, columnNames)
69+
70+
val table = JBTable(tableModel).apply {
71+
autoResizeMode = JTable.AUTO_RESIZE_ALL_COLUMNS
72+
}
73+
74+
val scrollPane = JBScrollPane(table).apply {
75+
preferredSize = Dimension(600, 300)
76+
}
77+
val errorLabel = JBLabel("Found Lint issue: ${errors.size}").apply {
78+
border = BorderFactory.createEmptyBorder(5, 5, 5, 5)
79+
}
80+
errorLabel.addMouseListener(object : MouseAdapter() {
81+
override fun mouseClicked(e: MouseEvent?) {
82+
createErrorButton(scrollPane, table, errors).showInCenterOf(panel)
83+
}
84+
85+
// hover also show tip
86+
override fun mouseEntered(e: MouseEvent) {
87+
errorLabel.toolTipText = "Click to view all errors"
88+
}
89+
})
90+
91+
panel.add(errorLabel)
92+
}
93+
94+
private fun createErrorButton(
95+
scrollPane: JBScrollPane,
96+
table: JBTable,
97+
errors: List<SketchInspectionError>
98+
): JBPopup = JBPopupFactory.getInstance()
99+
.createComponentPopupBuilder(scrollPane, table)
100+
.setTitle("Found Lint Issues: ${errors.size}")
101+
.setResizable(true)
102+
.setMovable(true)
103+
.setRequestFocus(true)
104+
.createPopup()
105+
}
31106

32107
object PsiErrorCollector {
33-
fun runInspections(project: Project, psiFile: PsiFile, originFile: VirtualFile): List<@InspectionMessage String> {
108+
fun runInspections(project: Project, psiFile: PsiFile, originFile: VirtualFile): List<SketchInspectionError> {
34109
val globalContext = InspectionManager.getInstance(project).createNewGlobalContext()
35110
as? GlobalInspectionContextBase ?: return emptyList()
36111

@@ -54,12 +129,11 @@ object PsiErrorCollector {
54129

55130
val problems = result.values.flatten()
56131
return@runReadAction problems.sortedBy { it.lineNumber }.distinctBy { it.lineNumber }.map {
57-
"Line ${it.lineNumber + 1}: ${it.descriptionTemplate}"
132+
SketchInspectionError.from(it)
58133
}
59134
}
60135
}
61136

62-
// skip NpmUsedModulesInstalled, ProblematicWhitespace, JSXUnresolvedComponent, UnterminatedStatement, UnterminatedStatementJS
63137
private val frontEndSkipError =
64138
setOf(
65139
"NpmUsedModulesInstalled",
@@ -76,9 +150,11 @@ object PsiErrorCollector {
76150
): MutableList<LocalInspectionToolWrapper> {
77151
val inspectionProfile = InspectionProjectProfileManager.getInstance(project).currentProfile
78152
val toolWrappers = inspectionProfile.getInspectionTools(psiFile)
79-
.filter { it.isApplicable(psiFile.language) }
80-
.filter { it.shortName !in frontEndSkipError }
81-
.filter { it.defaultLevel.severity == HighlightSeverity.WARNING || it.defaultLevel.severity == HighlightSeverity.ERROR }
153+
.filter {
154+
it.isApplicable(psiFile.language)
155+
&& it.displayKey?.id !in frontEndSkipError
156+
&& it.defaultLevel.severity == HighlightSeverity.WARNING || it.defaultLevel.severity == HighlightSeverity.ERROR
157+
}
82158

83159
toolWrappers.forEach {
84160
it.initialize(globalContext)

core/src/main/kotlin/cc/unitmesh/devti/sketch/ui/patch/SingleFileDiffSketch.kt

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cc.unitmesh.devti.sketch.ui.patch
22

33
import cc.unitmesh.devti.AutoDevBundle
44
import cc.unitmesh.devti.sketch.lint.PsiErrorCollector
5+
import cc.unitmesh.devti.sketch.lint.PsiErrorUI
56
import cc.unitmesh.devti.sketch.ui.LangSketch
67
import cc.unitmesh.devti.template.context.TemplateContext
78
import com.intellij.diff.editor.DiffVirtualFileBase
@@ -214,33 +215,14 @@ class SingleFileDiffSketch(
214215

215216
fun lintCheckForNewCode(currentFile: VirtualFile) {
216217
if (newCode.isEmpty()) return
217-
218218
val newFile = LightVirtualFile(currentFile, newCode, LocalTimeCounter.currentTime())
219219
val psiFile = runReadAction { PsiManager.getInstance(myProject).findFile(newFile) } ?: return
220-
val runInspections = PsiErrorCollector.runInspections(myProject, psiFile, currentFile)
221-
if (runInspections.isNotEmpty()) {
222-
showErrors(runInspections)
220+
val errors = PsiErrorCollector.runInspections(myProject, psiFile, currentFile)
221+
if (errors.isNotEmpty()) {
222+
PsiErrorUI.showErrors(errors, this@SingleFileDiffSketch.mainPanel)
223223
}
224224
}
225225

226-
private fun showErrors(errors: List<String>) {
227-
val errorPanel = JPanel(VerticalLayout(5)).apply {
228-
val errorLabel = JBLabel("Found Lint issue: ${errors.size}").apply {
229-
border = BorderFactory.createEmptyBorder(5, 5, 5, 5)
230-
}
231-
add(errorLabel)
232-
233-
errors.forEach { error ->
234-
val errorLabel = JBLabel(error).apply {
235-
border = BorderFactory.createEmptyBorder(5, 5, 5, 5)
236-
}
237-
add(errorLabel)
238-
}
239-
}
240-
241-
mainPanel.add(errorPanel)
242-
}
243-
244226
override fun dispose() {}
245227
}
246228

0 commit comments

Comments
 (0)