mirror of
https://github.com/chylex/IntelliJ-Inspection-Lens.git
synced 2024-11-24 22:42:52 +01:00
Compare commits
2 Commits
223fceb6b9
...
e28804ad5d
Author | SHA1 | Date | |
---|---|---|---|
e28804ad5d | |||
043c02e432 |
@ -1,10 +1,8 @@
|
|||||||
# Inspection Lens <img align="right" src="logo.png" alt="Plugin Logo">
|
# Inspection Lens <img align="right" src="logo.png" alt="Plugin Logo">
|
||||||
|
|
||||||
IntelliJ plugin that shows errors, warnings, and other inspection highlights inline.
|
Displays errors, warnings, and other inspections inline. Highlights the background of lines with inspections. Supports light and dark themes out of the box.
|
||||||
|
|
||||||
After installing the plugin, inspection descriptions will appear after the ends of lines, and the lines will be highlighted with a background color. Shown inspection severities are **Errors**, **Warnings**, **Weak Warnings**, **Server Problems**, **Grammar Errors**, **Typos**, and other inspections from plugins or future IntelliJ versions that have a high enough severity level. Each severity has a different color, with support for both light and dark themes.
|
By default, the plugin shows **Errors**, **Warnings**, **Weak Warnings**, **Server Problems**, **Grammar Errors**, **Typos**, and other inspections with a high enough severity level. Configure visible severities in **Settings | Tools | Inspection Lens**.
|
||||||
|
|
||||||
Note: The plugin is not customizable outside the ability to disable/enable the plugin without restarting the IDE. If the defaults don't work for you, I recommend trying the [Inline Error](https://plugins.jetbrains.com/plugin/17302-inlineerror) plugin which can be customized, building your own version of Inspection Lens, or proposing your change in the [issue tracker](https://github.com/chylex/IntelliJ-Inspection-Lens/issues).
|
|
||||||
|
|
||||||
Inspired by [Error Lens](https://marketplace.visualstudio.com/items?itemName=usernamehw.errorlens) for Visual Studio Code, and [Inline Error](https://plugins.jetbrains.com/plugin/17302-inlineerror) for IntelliJ Platform.
|
Inspired by [Error Lens](https://marketplace.visualstudio.com/items?itemName=usernamehw.errorlens) for Visual Studio Code, and [Inline Error](https://plugins.jetbrains.com/plugin/17302-inlineerror) for IntelliJ Platform.
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = "com.chylex.intellij.inspectionlens"
|
group = "com.chylex.intellij.inspectionlens"
|
||||||
version = "1.3.3"
|
version = "1.4"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
@ -83,6 +83,12 @@ class EditorLensManager private constructor(private val editor: Editor) {
|
|||||||
lensManager.hide(highlighter)
|
lensManager.hide(highlighter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object HideAll : Command {
|
||||||
|
override fun apply(lensManager: EditorLensManager) {
|
||||||
|
lensManager.hideAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -15,6 +15,10 @@ class EditorLensManagerDispatcher(private val lensManager: EditorLensManager) {
|
|||||||
enqueue(EditorLensManager.Command.Hide(highlighter))
|
enqueue(EditorLensManager.Command.Hide(highlighter))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun hideAll() {
|
||||||
|
enqueue(EditorLensManager.Command.HideAll)
|
||||||
|
}
|
||||||
|
|
||||||
private fun enqueue(item: EditorLensManager.Command) {
|
private fun enqueue(item: EditorLensManager.Command) {
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
queuedItems.add(item)
|
queuedItems.add(item)
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package com.chylex.intellij.inspectionlens.editor
|
package com.chylex.intellij.inspectionlens.editor
|
||||||
|
|
||||||
|
import com.chylex.intellij.inspectionlens.settings.LensSettingsState
|
||||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo
|
import com.intellij.codeInsight.daemon.impl.HighlightInfo
|
||||||
import com.intellij.lang.annotation.HighlightSeverity
|
|
||||||
import com.intellij.openapi.Disposable
|
import com.intellij.openapi.Disposable
|
||||||
|
import com.intellij.openapi.components.service
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.openapi.editor.ex.MarkupModelEx
|
import com.intellij.openapi.editor.ex.MarkupModelEx
|
||||||
import com.intellij.openapi.editor.ex.RangeHighlighterEx
|
import com.intellij.openapi.editor.ex.RangeHighlighterEx
|
||||||
@ -48,12 +49,16 @@ internal class LensMarkupModelListener private constructor(editor: Editor) : Mar
|
|||||||
highlighters.forEach(::showIfValid)
|
highlighters.forEach(::showIfValid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun hideAll() {
|
||||||
|
lensManagerDispatcher.hideAll()
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val EDITOR_KEY = Key<LensMarkupModelListener>(LensMarkupModelListener::class.java.name)
|
private val EDITOR_KEY = Key<LensMarkupModelListener>(LensMarkupModelListener::class.java.name)
|
||||||
private val MINIMUM_SEVERITY = HighlightSeverity.TEXT_ATTRIBUTES.myVal + 1
|
private val SETTINGS_SERVICE = service<LensSettingsState>()
|
||||||
|
|
||||||
private fun getFilteredHighlightInfo(highlighter: RangeHighlighter): HighlightInfo? {
|
private fun getFilteredHighlightInfo(highlighter: RangeHighlighter): HighlightInfo? {
|
||||||
return HighlightInfo.fromRangeHighlighter(highlighter)?.takeIf { it.severity.myVal >= MINIMUM_SEVERITY }
|
return HighlightInfo.fromRangeHighlighter(highlighter)?.takeIf { SETTINGS_SERVICE.severityFilter.test(it.severity) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline fun runWithHighlighterIfValid(highlighter: RangeHighlighter, actionForImmediate: (HighlighterWithInfo) -> Unit, actionForAsync: (HighlighterWithInfo.Async) -> Unit) {
|
private inline fun runWithHighlighterIfValid(highlighter: RangeHighlighter, actionForImmediate: (HighlighterWithInfo) -> Unit, actionForAsync: (HighlighterWithInfo.Async) -> Unit) {
|
||||||
@ -101,6 +106,7 @@ internal class LensMarkupModelListener private constructor(editor: Editor) : Mar
|
|||||||
val listener = editor.getUserData(EDITOR_KEY) ?: return
|
val listener = editor.getUserData(EDITOR_KEY) ?: return
|
||||||
val markupModel = getMarkupModel(editor) ?: return
|
val markupModel = getMarkupModel(editor) ?: return
|
||||||
|
|
||||||
|
listener.hideAll()
|
||||||
listener.showAllValid(markupModel.allHighlighters)
|
listener.showAllValid(markupModel.allHighlighters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
package com.chylex.intellij.inspectionlens.editor
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.daemon.impl.SeverityRegistrar
|
||||||
|
import com.intellij.lang.annotation.HighlightSeverity
|
||||||
|
import java.util.function.Predicate
|
||||||
|
|
||||||
|
class LensSeverityFilter(private val hiddenSeverityIds: Set<String>, private val showUnknownSeverities: Boolean) : Predicate<HighlightSeverity> {
|
||||||
|
private val knownSeverityIds = getSupportedSeverities().mapTo(HashSet(), HighlightSeverity::getName)
|
||||||
|
|
||||||
|
override fun test(severity: HighlightSeverity): Boolean {
|
||||||
|
if (!isSupported(severity)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return if (severity.name in knownSeverityIds)
|
||||||
|
severity.name !in hiddenSeverityIds
|
||||||
|
else
|
||||||
|
showUnknownSeverities
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
private fun isSupported(severity: HighlightSeverity): Boolean {
|
||||||
|
return severity > HighlightSeverity.TEXT_ATTRIBUTES && severity !== HighlightSeverity.INFO
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSupportedSeverities(registrar: SeverityRegistrar = SeverityRegistrar.getSeverityRegistrar(null)): List<HighlightSeverity> {
|
||||||
|
return registrar.allSeverities.filter(::isSupported)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,127 @@
|
|||||||
|
package com.chylex.intellij.inspectionlens.settings
|
||||||
|
|
||||||
|
import com.chylex.intellij.inspectionlens.editor.LensSeverityFilter
|
||||||
|
import com.intellij.codeInsight.daemon.impl.SeverityRegistrar
|
||||||
|
import com.intellij.lang.annotation.HighlightSeverity
|
||||||
|
import com.intellij.openapi.components.service
|
||||||
|
import com.intellij.openapi.editor.event.SelectionEvent
|
||||||
|
import com.intellij.openapi.editor.event.SelectionListener
|
||||||
|
import com.intellij.openapi.editor.markup.TextAttributes
|
||||||
|
import com.intellij.openapi.options.BoundConfigurable
|
||||||
|
import com.intellij.openapi.options.ConfigurableWithId
|
||||||
|
import com.intellij.openapi.ui.DialogPanel
|
||||||
|
import com.intellij.openapi.util.Disposer
|
||||||
|
import com.intellij.ui.DisabledTraversalPolicy
|
||||||
|
import com.intellij.ui.EditorTextFieldCellRenderer.SimpleRendererComponent
|
||||||
|
import com.intellij.ui.components.JBCheckBox
|
||||||
|
import com.intellij.ui.dsl.builder.Cell
|
||||||
|
import com.intellij.ui.dsl.builder.RightGap
|
||||||
|
import com.intellij.ui.dsl.builder.Row
|
||||||
|
import com.intellij.ui.dsl.builder.RowLayout
|
||||||
|
import com.intellij.ui.dsl.builder.bindSelected
|
||||||
|
import com.intellij.ui.dsl.builder.panel
|
||||||
|
import java.awt.Cursor
|
||||||
|
|
||||||
|
class LensApplicationConfigurable : BoundConfigurable("Inspection Lens"), ConfigurableWithId {
|
||||||
|
companion object {
|
||||||
|
const val ID = "InspectionLens"
|
||||||
|
|
||||||
|
private fun getTextAttributes(registrar: SeverityRegistrar, severity: HighlightSeverity): TextAttributes? {
|
||||||
|
return registrar.getHighlightInfoTypeBySeverity(severity).attributesKey.defaultAttributes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class DisplayedSeverity(
|
||||||
|
val id: String,
|
||||||
|
val severity: StoredSeverity,
|
||||||
|
val textAttributes: TextAttributes? = null,
|
||||||
|
) {
|
||||||
|
constructor(
|
||||||
|
severity: HighlightSeverity,
|
||||||
|
registrar: SeverityRegistrar,
|
||||||
|
) : this(
|
||||||
|
id = severity.name,
|
||||||
|
severity = StoredSeverity(severity),
|
||||||
|
textAttributes = getTextAttributes(registrar, severity)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val settingsService = service<LensSettingsState>()
|
||||||
|
|
||||||
|
private val allSeverities by lazy(LazyThreadSafetyMode.NONE) {
|
||||||
|
val settings = settingsService.state
|
||||||
|
val registrar = SeverityRegistrar.getSeverityRegistrar(null)
|
||||||
|
|
||||||
|
val knownSeverities = LensSeverityFilter.getSupportedSeverities(registrar).map { DisplayedSeverity(it, registrar) }
|
||||||
|
val knownSeverityIds = knownSeverities.mapTo(HashSet(), DisplayedSeverity::id)
|
||||||
|
|
||||||
|
// Update names and priorities of stored severities.
|
||||||
|
for ((id, knownSeverity, _) in knownSeverities) {
|
||||||
|
val storedSeverity = settings.hiddenSeverities[id]
|
||||||
|
if (storedSeverity != null && storedSeverity != knownSeverity) {
|
||||||
|
settings.hiddenSeverities[id] = knownSeverity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val unknownSeverities = settings.hiddenSeverities.entries
|
||||||
|
.filterNot { it.key in knownSeverityIds }
|
||||||
|
.map { DisplayedSeverity(it.key, it.value) }
|
||||||
|
|
||||||
|
(knownSeverities + unknownSeverities).sortedByDescending { it.severity.priority }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getId(): String {
|
||||||
|
return ID
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createPanel(): DialogPanel {
|
||||||
|
val settings = settingsService.state
|
||||||
|
|
||||||
|
return panel {
|
||||||
|
group("Shown Severities") {
|
||||||
|
for ((id, severity, textAttributes) in allSeverities) {
|
||||||
|
row {
|
||||||
|
checkBox(severity.name)
|
||||||
|
.bindSelectedToNotIn(settings.hiddenSeverities, id, severity)
|
||||||
|
.gap(RightGap.COLUMNS)
|
||||||
|
|
||||||
|
labelWithAttributes("Example", textAttributes)
|
||||||
|
}.layout(RowLayout.PARENT_GRID)
|
||||||
|
}
|
||||||
|
|
||||||
|
row {
|
||||||
|
checkBox("Other").bindSelected(settings::showUnknownSeverities)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <K, V> Cell<JBCheckBox>.bindSelectedToNotIn(collection: MutableMap<K, V>, key: K, value: V): Cell<JBCheckBox> {
|
||||||
|
return bindSelected({ key !in collection }, { if (it) collection.remove(key) else collection[key] = value })
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Row.labelWithAttributes(text: String, textAttributes: TextAttributes?): Cell<SimpleRendererComponent> {
|
||||||
|
val label = SimpleRendererComponent(null, null, true)
|
||||||
|
label.setText(text, textAttributes, false)
|
||||||
|
label.focusTraversalPolicy = DisabledTraversalPolicy()
|
||||||
|
|
||||||
|
val editor = label.editor
|
||||||
|
editor.setCustomCursor(this, Cursor.getDefaultCursor())
|
||||||
|
editor.contentComponent.setOpaque(false)
|
||||||
|
editor.selectionModel.addSelectionListener(object : SelectionListener {
|
||||||
|
override fun selectionChanged(e: SelectionEvent) {
|
||||||
|
if (!e.newRange.isEmpty) {
|
||||||
|
editor.selectionModel.removeSelection(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
Disposer.register(disposable!!, label)
|
||||||
|
return cell(label)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun apply() {
|
||||||
|
super.apply()
|
||||||
|
settingsService.update()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
package com.chylex.intellij.inspectionlens.settings
|
||||||
|
|
||||||
|
import com.chylex.intellij.inspectionlens.InspectionLensRefresher
|
||||||
|
import com.chylex.intellij.inspectionlens.editor.LensSeverityFilter
|
||||||
|
import com.intellij.openapi.components.BaseState
|
||||||
|
import com.intellij.openapi.components.SettingsCategory
|
||||||
|
import com.intellij.openapi.components.SimplePersistentStateComponent
|
||||||
|
import com.intellij.openapi.components.State
|
||||||
|
import com.intellij.openapi.components.Storage
|
||||||
|
import com.intellij.util.xmlb.annotations.XMap
|
||||||
|
|
||||||
|
@State(
|
||||||
|
name = LensApplicationConfigurable.ID,
|
||||||
|
storages = [ Storage("chylex.inspectionLens.xml") ],
|
||||||
|
category = SettingsCategory.UI
|
||||||
|
)
|
||||||
|
class LensSettingsState : SimplePersistentStateComponent<LensSettingsState.State>(State()) {
|
||||||
|
class State : BaseState() {
|
||||||
|
@get:XMap
|
||||||
|
val hiddenSeverities by map<String, StoredSeverity>()
|
||||||
|
|
||||||
|
var showUnknownSeverities by property(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Synchronized
|
||||||
|
@set:Synchronized
|
||||||
|
var severityFilter = createSeverityFilter()
|
||||||
|
private set
|
||||||
|
|
||||||
|
override fun loadState(state: State) {
|
||||||
|
super.loadState(state)
|
||||||
|
update()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun update() {
|
||||||
|
severityFilter = createSeverityFilter()
|
||||||
|
InspectionLensRefresher.scheduleRefresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createSeverityFilter(): LensSeverityFilter {
|
||||||
|
val state = state
|
||||||
|
return LensSeverityFilter(state.hiddenSeverities.keys, state.showUnknownSeverities)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package com.chylex.intellij.inspectionlens.settings
|
||||||
|
|
||||||
|
import com.intellij.lang.annotation.HighlightSeverity
|
||||||
|
import com.intellij.util.xmlb.annotations.Tag
|
||||||
|
|
||||||
|
@Tag("severity")
|
||||||
|
data class StoredSeverity(var name: String = "", var priority: Int = 0) {
|
||||||
|
constructor(severity: HighlightSeverity) : this(severity.displayCapitalizedName, severity.myVal)
|
||||||
|
}
|
@ -4,19 +4,18 @@
|
|||||||
<vendor url="https://chylex.com">chylex</vendor>
|
<vendor url="https://chylex.com">chylex</vendor>
|
||||||
|
|
||||||
<description><![CDATA[
|
<description><![CDATA[
|
||||||
Shows errors, warnings, and other inspection highlights inline.
|
Displays errors, warnings, and other inspections inline. Highlights the background of lines with inspections. Supports light and dark themes out of the box.
|
||||||
<br><br>
|
<br><br>
|
||||||
After installing the plugin, inspection descriptions will appear after the ends of lines, and the lines will be highlighted with a background color.
|
By default, the plugin shows <b>Errors</b>, <b>Warnings</b>, <b>Weak Warnings</b>, <b>Server Problems</b>, <b>Grammar Errors</b>, <b>Typos</b>, and other inspections with a high enough severity level. Configure visible severities in <b>Settings | Tools | Inspection Lens</a>.
|
||||||
Shown inspection severities are <b>Errors</b>, <b>Warnings</b>, <b>Weak Warnings</b>, <b>Server Problems</b>, <b>Grammar Errors</b>, <b>Typos</b>, and other inspections from plugins or future IntelliJ versions that have a high enough severity level.
|
|
||||||
Each severity has a different color, with support for both light and dark themes.
|
|
||||||
<br><br>
|
|
||||||
Note: The plugin is not customizable outside the ability to disable/enable the plugin without restarting the IDE.
|
|
||||||
If the defaults don't work for you, I recommend trying the <a href="https://plugins.jetbrains.com/plugin/17302-inlineerror">Inline Error</a> plugin which can be customized, building your own version of Inspection Lens, or proposing your change in the <a href="https://github.com/chylex/IntelliJ-Inspection-Lens/issues">issue tracker</a>.
|
|
||||||
<br><br>
|
<br><br>
|
||||||
Inspired by <a href="https://marketplace.visualstudio.com/items?itemName=usernamehw.errorlens">Error Lens</a> for VS Code, and <a href="https://plugins.jetbrains.com/plugin/17302-inlineerror">Inline Error</a> for IntelliJ Platform.
|
Inspired by <a href="https://marketplace.visualstudio.com/items?itemName=usernamehw.errorlens">Error Lens</a> for VS Code, and <a href="https://plugins.jetbrains.com/plugin/17302-inlineerror">Inline Error</a> for IntelliJ Platform.
|
||||||
]]></description>
|
]]></description>
|
||||||
|
|
||||||
<change-notes><![CDATA[
|
<change-notes><![CDATA[
|
||||||
|
<b>Version 1.4</b>
|
||||||
|
<ul>
|
||||||
|
<li>Added configuration of visible severities to <b>Settings | Tools | Inspection Lens</b>.</li>
|
||||||
|
</ul>
|
||||||
<b>Version 1.3.3</b>
|
<b>Version 1.3.3</b>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Partially reverted fix for inspections that include HTML in their description due to breaking inspections with angled brackets.</li>
|
<li>Partially reverted fix for inspections that include HTML in their description due to breaking inspections with angled brackets.</li>
|
||||||
@ -63,6 +62,14 @@
|
|||||||
<depends>com.intellij.modules.platform</depends>
|
<depends>com.intellij.modules.platform</depends>
|
||||||
<depends optional="true" config-file="compatibility/InspectionLens-Grazie.xml">tanvd.grazi</depends>
|
<depends optional="true" config-file="compatibility/InspectionLens-Grazie.xml">tanvd.grazi</depends>
|
||||||
|
|
||||||
|
<extensions defaultExtensionNs="com.intellij">
|
||||||
|
<applicationService serviceImplementation="com.chylex.intellij.inspectionlens.settings.LensSettingsState" />
|
||||||
|
<applicationConfigurable id="com.chylex.intellij.inspectionlens.settings.LensApplicationConfigurable"
|
||||||
|
instance="com.chylex.intellij.inspectionlens.settings.LensApplicationConfigurable"
|
||||||
|
displayName="Inspection Lens"
|
||||||
|
parentId="tools" />
|
||||||
|
</extensions>
|
||||||
|
|
||||||
<applicationListeners>
|
<applicationListeners>
|
||||||
<listener class="com.chylex.intellij.inspectionlens.InspectionLensPluginListener" topic="com.intellij.ide.plugins.DynamicPluginListener" />
|
<listener class="com.chylex.intellij.inspectionlens.InspectionLensPluginListener" topic="com.intellij.ide.plugins.DynamicPluginListener" />
|
||||||
</applicationListeners>
|
</applicationListeners>
|
||||||
|
Loading…
Reference in New Issue
Block a user