1
0
mirror of https://github.com/chylex/IntelliJ-Inspection-Lens.git synced 2025-01-31 06:46:06 +01:00

Compare commits

..

No commits in common. "632a052ff90e2ef025bb6ae34ca443806038bc75" and "624254fba3850d926634ab2f223294f022ebeca6" have entirely different histories.

22 changed files with 146 additions and 130 deletions

1
.gitignore vendored
View File

@ -1,5 +1,4 @@
/.idea/* /.idea/*
!/.idea/dictionaries
!/.idea/runConfigurations !/.idea/runConfigurations
/.gradle/ /.gradle/

View File

@ -1,7 +0,0 @@
<component name="ProjectDictionaryState">
<dictionary name="default.user">
<words>
<w>inspectionlens</w>
</words>
</dictionary>
</component>

View File

@ -1,11 +1,15 @@
package com.chylex.intellij.inspectionlens package com.chylex.intellij.inspectionlens
import com.chylex.intellij.inspectionlens.editor.EditorLensFeatures import com.chylex.intellij.inspectionlens.editor.EditorLensManager
import com.chylex.intellij.inspectionlens.editor.LensMarkupModelListener
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.service
import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.fileEditor.TextEditor import com.intellij.openapi.fileEditor.TextEditor
import com.intellij.openapi.project.ProjectManager import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.rd.createLifetime
import com.intellij.openapi.rd.createNestedDisposable
import com.jetbrains.rd.util.lifetime.Lifetime
/** /**
* Handles installation and uninstallation of plugin features in editors. * Handles installation and uninstallation of plugin features in editors.
@ -17,7 +21,7 @@ internal object InspectionLens {
* Installs lenses into [editor]. * Installs lenses into [editor].
*/ */
fun install(editor: TextEditor) { fun install(editor: TextEditor) {
EditorLensFeatures.install(editor.editor, service<InspectionLensPluginDisposableService>().intersect(editor)) LensMarkupModelListener.register(editor.editor, createEditorDisposable(editor))
} }
/** /**
@ -28,19 +32,30 @@ internal object InspectionLens {
} }
/** /**
* Refreshes lenses in all open editors. * Uninstalls lenses from all open editors.
*/ */
private fun refresh() { fun uninstall() {
forEachOpenEditor { forEachOpenEditor {
EditorLensFeatures.refresh(it.editor) EditorLensManager.remove(it.editor)
} }
} }
/** /**
* Schedules a refresh of lenses in all open editors. * Refreshes lenses in all open editors.
*/ */
fun scheduleRefresh() { fun refresh() {
Refresh.schedule() forEachOpenEditor {
LensMarkupModelListener.refresh(it.editor)
}
}
/**
* Creates a [Disposable] that will be disposed when either the [TextEditor] is disposed or the plugin is unloaded.
*/
private fun createEditorDisposable(textEditor: TextEditor): Disposable {
val pluginLifetime = ApplicationManager.getApplication().getService(InspectionLensPluginDisposableService::class.java).createLifetime()
val editorLifetime = textEditor.createLifetime()
return Lifetime.intersect(pluginLifetime, editorLifetime).createNestedDisposable("InspectionLensIntersectedLifetime")
} }
/** /**
@ -55,26 +70,4 @@ internal object InspectionLens {
} }
} }
} }
private object Refresh {
private var needsRefresh = false
fun schedule() {
synchronized(this) {
if (!needsRefresh) {
needsRefresh = true
ApplicationManager.getApplication().invokeLater(this::run)
}
}
}
private fun run() {
synchronized(this) {
if (needsRefresh) {
needsRefresh = false
refresh()
}
}
}
}
} }

View File

@ -2,21 +2,11 @@ package com.chylex.intellij.inspectionlens
import com.intellij.openapi.Disposable import com.intellij.openapi.Disposable
import com.intellij.openapi.components.Service import com.intellij.openapi.components.Service
import com.intellij.openapi.rd.createLifetime
import com.intellij.openapi.rd.createNestedDisposable
import com.jetbrains.rd.util.lifetime.Lifetime
/** /**
* Gets automatically disposed when the plugin is unloaded. * Gets automatically disposed when the plugin is unloaded.
*/ */
@Service @Service
class InspectionLensPluginDisposableService : Disposable { class InspectionLensPluginDisposableService : Disposable {
/**
* Creates a [Disposable] that will be disposed when either plugin is unloaded, or the [other] [Disposable] is disposed.
*/
fun intersect(other: Disposable): Disposable {
return Lifetime.intersect(createLifetime(), other.createLifetime()).createNestedDisposable("InspectionLensIntersectedLifetime")
}
override fun dispose() {} override fun dispose() {}
} }

View File

@ -4,7 +4,7 @@ import com.intellij.ide.plugins.DynamicPluginListener
import com.intellij.ide.plugins.IdeaPluginDescriptor import com.intellij.ide.plugins.IdeaPluginDescriptor
/** /**
* Installs [InspectionLens] in open editors when the plugin is loaded. * Installs [InspectionLens] in open editors when the plugin is loaded, and uninstalls it when the plugin is unloaded.
*/ */
class InspectionLensPluginListener : DynamicPluginListener { class InspectionLensPluginListener : DynamicPluginListener {
override fun pluginLoaded(pluginDescriptor: IdeaPluginDescriptor) { override fun pluginLoaded(pluginDescriptor: IdeaPluginDescriptor) {
@ -12,4 +12,10 @@ class InspectionLensPluginListener : DynamicPluginListener {
InspectionLens.install() InspectionLens.install()
} }
} }
override fun beforePluginUnload(pluginDescriptor: IdeaPluginDescriptor, isUpdate: Boolean) {
if (pluginDescriptor.pluginId.idString == InspectionLens.PLUGIN_ID) {
InspectionLens.uninstall()
}
}
} }

View File

@ -0,0 +1,25 @@
package com.chylex.intellij.inspectionlens
import com.intellij.openapi.application.ApplicationManager
object InspectionLensRefresher {
private var needsRefresh = false
fun scheduleRefresh() {
synchronized(this) {
if (!needsRefresh) {
needsRefresh = true
ApplicationManager.getApplication().invokeLater(::refresh)
}
}
}
private fun refresh() {
synchronized(this) {
if (needsRefresh) {
needsRefresh = false
InspectionLens.refresh()
}
}
}
}

View File

@ -1,6 +1,6 @@
package com.chylex.intellij.inspectionlens.compatibility package com.chylex.intellij.inspectionlens.compatibility
import com.chylex.intellij.inspectionlens.editor.lens.LensSeverity import com.chylex.intellij.inspectionlens.editor.LensSeverity
import com.intellij.grazie.ide.TextProblemSeverities import com.intellij.grazie.ide.TextProblemSeverities
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.ProjectActivity import com.intellij.openapi.startup.ProjectActivity

View File

@ -1,12 +1,12 @@
package com.chylex.intellij.inspectionlens.compatibility package com.chylex.intellij.inspectionlens.compatibility
import com.chylex.intellij.inspectionlens.editor.lens.LensSeverity import com.chylex.intellij.inspectionlens.editor.LensSeverity
import com.intellij.lang.annotation.HighlightSeverity import com.intellij.lang.annotation.HighlightSeverity
import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.diagnostic.logger
import com.intellij.profile.codeInspection.InspectionProfileManager import com.intellij.profile.codeInspection.InspectionProfileManager
import com.intellij.spellchecker.SpellCheckerSeveritiesProvider import com.intellij.spellchecker.SpellCheckerSeveritiesProvider
internal object SpellCheckerSupport { object SpellCheckerSupport {
private val log = logger<SpellCheckerSupport>() private val log = logger<SpellCheckerSupport>()
fun load() { fun load() {

View File

@ -1,4 +1,4 @@
package com.chylex.intellij.inspectionlens.editor.lens package com.chylex.intellij.inspectionlens.editor
import com.chylex.intellij.inspectionlens.settings.LensSettingsState import com.chylex.intellij.inspectionlens.settings.LensSettingsState
import com.intellij.codeInsight.daemon.impl.HighlightInfo import com.intellij.codeInsight.daemon.impl.HighlightInfo

View File

@ -1,51 +0,0 @@
package com.chylex.intellij.inspectionlens.editor
import com.intellij.openapi.Disposable
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.ex.MarkupModelEx
import com.intellij.openapi.editor.impl.DocumentMarkupModel
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.Key
/**
* Manages Inspection Lens features for a single [Editor].
*/
internal class EditorLensFeatures private constructor(
editor: Editor,
private val markupModel: MarkupModelEx,
disposable: Disposable
) {
private val lensManager = EditorLensManager(editor)
private val lensManagerDispatcher = EditorLensManagerDispatcher(lensManager)
private val markupModelListener = LensMarkupModelListener(lensManagerDispatcher)
init {
markupModel.addMarkupModelListener(disposable, markupModelListener)
markupModelListener.showAllValid(markupModel.allHighlighters)
}
private fun refresh() {
markupModelListener.hideAll()
markupModelListener.showAllValid(markupModel.allHighlighters)
}
companion object {
private val EDITOR_KEY = Key<EditorLensFeatures>(EditorLensFeatures::class.java.name)
fun install(editor: Editor, disposable: Disposable) {
if (editor.getUserData(EDITOR_KEY) != null) {
return
}
val markupModel = DocumentMarkupModel.forDocument(editor.document, editor.project, false) as? MarkupModelEx ?: return
val features = EditorLensFeatures(editor, markupModel, disposable)
editor.putUserData(EDITOR_KEY, features)
Disposer.register(disposable) { editor.putUserData(EDITOR_KEY, null) }
}
fun refresh(editor: Editor) {
editor.getUserData(EDITOR_KEY)?.refresh()
}
}
}

View File

@ -1,4 +1,4 @@
package com.chylex.intellij.inspectionlens.editor.lens package com.chylex.intellij.inspectionlens.editor
import com.chylex.intellij.inspectionlens.settings.LensSettingsState import com.chylex.intellij.inspectionlens.settings.LensSettingsState
import com.intellij.codeInsight.daemon.impl.HighlightInfo import com.intellij.codeInsight.daemon.impl.HighlightInfo

View File

@ -1,4 +1,4 @@
package com.chylex.intellij.inspectionlens.editor.lens package com.chylex.intellij.inspectionlens.editor
import com.intellij.codeInsight.daemon.impl.HighlightInfo import com.intellij.codeInsight.daemon.impl.HighlightInfo
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor

View File

@ -1,16 +1,32 @@
package com.chylex.intellij.inspectionlens.editor package com.chylex.intellij.inspectionlens.editor
import com.chylex.intellij.inspectionlens.editor.lens.EditorLens
import com.chylex.intellij.inspectionlens.settings.LensSettingsState import com.chylex.intellij.inspectionlens.settings.LensSettingsState
import com.intellij.openapi.components.service import com.intellij.openapi.components.service
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.markup.RangeHighlighter import com.intellij.openapi.editor.markup.RangeHighlighter
import com.intellij.openapi.util.Key
import java.util.IdentityHashMap import java.util.IdentityHashMap
/** /**
* Manages visible inspection lenses for an [Editor]. * Manages visible inspection lenses for an [Editor].
*/ */
internal class EditorLensManager(private val editor: Editor) { class EditorLensManager private constructor(private val editor: Editor) {
companion object {
private val EDITOR_KEY = Key<EditorLensManager>(EditorLensManager::class.java.name)
fun getOrCreate(editor: Editor): EditorLensManager {
return editor.getUserData(EDITOR_KEY) ?: EditorLensManager(editor).also { editor.putUserData(EDITOR_KEY, it) }
}
fun remove(editor: Editor) {
val manager = editor.getUserData(EDITOR_KEY)
if (manager != null) {
manager.hideAll()
editor.putUserData(EDITOR_KEY, null)
}
}
}
private val lenses = IdentityHashMap<RangeHighlighter, EditorLens>() private val lenses = IdentityHashMap<RangeHighlighter, EditorLens>()
private val settings = service<LensSettingsState>() private val settings = service<LensSettingsState>()

View File

@ -3,7 +3,7 @@ package com.chylex.intellij.inspectionlens.editor
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.markup.RangeHighlighter import com.intellij.openapi.editor.markup.RangeHighlighter
internal class EditorLensManagerDispatcher(private val lensManager: EditorLensManager) { class EditorLensManagerDispatcher(private val lensManager: EditorLensManager) {
private var queuedItems = mutableListOf<EditorLensManager.Command>() private var queuedItems = mutableListOf<EditorLensManager.Command>()
private var isEnqueued = false private var isEnqueued = false

View File

@ -2,16 +2,23 @@ package com.chylex.intellij.inspectionlens.editor
import com.chylex.intellij.inspectionlens.settings.LensSettingsState import com.chylex.intellij.inspectionlens.settings.LensSettingsState
import com.intellij.codeInsight.daemon.impl.HighlightInfo import com.intellij.codeInsight.daemon.impl.HighlightInfo
import com.intellij.openapi.Disposable
import com.intellij.openapi.components.service import com.intellij.openapi.components.service
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.ex.MarkupModelEx
import com.intellij.openapi.editor.ex.RangeHighlighterEx import com.intellij.openapi.editor.ex.RangeHighlighterEx
import com.intellij.openapi.editor.impl.DocumentMarkupModel
import com.intellij.openapi.editor.impl.event.MarkupModelListener import com.intellij.openapi.editor.impl.event.MarkupModelListener
import com.intellij.openapi.editor.markup.RangeHighlighter import com.intellij.openapi.editor.markup.RangeHighlighter
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.Key
/** /**
* Listens for inspection highlights and reports them to [EditorLensManager]. * Listens for inspection highlights and reports them to [EditorLensManager].
*/ */
internal class LensMarkupModelListener(private val lensManagerDispatcher: EditorLensManagerDispatcher) : MarkupModelListener { internal class LensMarkupModelListener private constructor(editor: Editor) : MarkupModelListener {
private val settings = service<LensSettingsState>() private val settings = service<LensSettingsState>()
private val lensManagerDispatcher = EditorLensManagerDispatcher(EditorLensManager.getOrCreate(editor))
override fun afterAdded(highlighter: RangeHighlighterEx) { override fun afterAdded(highlighter: RangeHighlighterEx) {
showIfValid(highlighter) showIfValid(highlighter)
@ -39,11 +46,11 @@ internal class LensMarkupModelListener(private val lensManagerDispatcher: Editor
} }
} }
fun showAllValid(highlighters: Array<RangeHighlighter>) { private fun showAllValid(highlighters: Array<RangeHighlighter>) {
highlighters.forEach(::showIfValid) highlighters.forEach(::showIfValid)
} }
fun hideAll() { private fun hideAll() {
lensManagerDispatcher.hideAll() lensManagerDispatcher.hideAll()
} }
@ -58,12 +65,49 @@ internal class LensMarkupModelListener(private val lensManagerDispatcher: Editor
} }
} }
private inline fun processHighlighterWithInfo(highlighterWithInfo: HighlighterWithInfo, actionForImmediate: (HighlighterWithInfo) -> Unit, actionForAsync: (HighlighterWithInfo.Async) -> Unit) { companion object {
if (highlighterWithInfo is HighlighterWithInfo.Async) { private val EDITOR_KEY = Key<LensMarkupModelListener>(LensMarkupModelListener::class.java.name)
actionForAsync(highlighterWithInfo)
private inline fun processHighlighterWithInfo(highlighterWithInfo: HighlighterWithInfo, actionForImmediate: (HighlighterWithInfo) -> Unit, actionForAsync: (HighlighterWithInfo.Async) -> Unit) {
if (highlighterWithInfo is HighlighterWithInfo.Async) {
actionForAsync(highlighterWithInfo)
}
else if (highlighterWithInfo.hasDescription) {
actionForImmediate(highlighterWithInfo)
}
} }
else if (highlighterWithInfo.hasDescription) {
actionForImmediate(highlighterWithInfo) private fun getMarkupModel(editor: Editor): MarkupModelEx? {
return DocumentMarkupModel.forDocument(editor.document, editor.project, false) as? MarkupModelEx
}
/**
* Attaches a new [LensMarkupModelListener] to the [Editor], and reports all existing inspection highlights to [EditorLensManager].
*/
fun register(editor: Editor, disposable: Disposable) {
if (editor.getUserData(EDITOR_KEY) != null) {
return
}
val markupModel = getMarkupModel(editor) ?: return
val listener = LensMarkupModelListener(editor)
editor.putUserData(EDITOR_KEY, listener)
Disposer.register(disposable) { editor.putUserData(EDITOR_KEY, null) }
markupModel.addMarkupModelListener(disposable, listener)
listener.showAllValid(markupModel.allHighlighters)
}
/**
* Recreates all inspection highlights in the [Editor].
*/
fun refresh(editor: Editor) {
val listener = editor.getUserData(EDITOR_KEY) ?: return
val markupModel = getMarkupModel(editor) ?: return
listener.hideAll()
listener.showAllValid(markupModel.allHighlighters)
} }
} }
} }

View File

@ -1,4 +1,4 @@
package com.chylex.intellij.inspectionlens.editor.lens package com.chylex.intellij.inspectionlens.editor
import com.chylex.intellij.inspectionlens.settings.LensSettingsState import com.chylex.intellij.inspectionlens.settings.LensSettingsState
import com.intellij.codeInsight.daemon.impl.HighlightInfo import com.intellij.codeInsight.daemon.impl.HighlightInfo

View File

@ -1,6 +1,6 @@
package com.chylex.intellij.inspectionlens.editor.lens package com.chylex.intellij.inspectionlens.editor
import com.chylex.intellij.inspectionlens.InspectionLens import com.chylex.intellij.inspectionlens.InspectionLensRefresher
import com.chylex.intellij.inspectionlens.compatibility.SpellCheckerSupport import com.chylex.intellij.inspectionlens.compatibility.SpellCheckerSupport
import com.intellij.lang.annotation.HighlightSeverity import com.intellij.lang.annotation.HighlightSeverity
import com.intellij.ui.ColorUtil import com.intellij.ui.ColorUtil
@ -54,7 +54,7 @@ enum class LensSeverity(baseColor: Color, lightThemeDarkening: Int, darkThemeBri
*/ */
internal fun registerMapping(severity: HighlightSeverity, lensSeverity: LensSeverity) { internal fun registerMapping(severity: HighlightSeverity, lensSeverity: LensSeverity) {
if (mapping.put(severity, lensSeverity) != lensSeverity) { if (mapping.put(severity, lensSeverity) != lensSeverity) {
InspectionLens.scheduleRefresh() InspectionLensRefresher.scheduleRefresh()
} }
} }

View File

@ -1,4 +1,4 @@
package com.chylex.intellij.inspectionlens.editor.lens package com.chylex.intellij.inspectionlens.editor
import com.intellij.codeInsight.daemon.impl.SeverityRegistrar import com.intellij.codeInsight.daemon.impl.SeverityRegistrar
import com.intellij.lang.annotation.HighlightSeverity import com.intellij.lang.annotation.HighlightSeverity

View File

@ -1,4 +1,4 @@
package com.chylex.intellij.inspectionlens.editor.lens package com.chylex.intellij.inspectionlens.editor
import com.intellij.openapi.editor.markup.UnmodifiableTextAttributes import com.intellij.openapi.editor.markup.UnmodifiableTextAttributes
import com.intellij.ui.JBColor import com.intellij.ui.JBColor

View File

@ -1,6 +1,6 @@
package com.chylex.intellij.inspectionlens.settings package com.chylex.intellij.inspectionlens.settings
import com.chylex.intellij.inspectionlens.editor.lens.LensSeverityFilter import com.chylex.intellij.inspectionlens.editor.LensSeverityFilter
import com.intellij.codeInsight.daemon.impl.SeverityRegistrar import com.intellij.codeInsight.daemon.impl.SeverityRegistrar
import com.intellij.lang.annotation.HighlightSeverity import com.intellij.lang.annotation.HighlightSeverity
import com.intellij.openapi.components.service import com.intellij.openapi.components.service

View File

@ -1,7 +1,7 @@
package com.chylex.intellij.inspectionlens.settings package com.chylex.intellij.inspectionlens.settings
import com.chylex.intellij.inspectionlens.InspectionLens import com.chylex.intellij.inspectionlens.InspectionLensRefresher
import com.chylex.intellij.inspectionlens.editor.lens.LensSeverityFilter import com.chylex.intellij.inspectionlens.editor.LensSeverityFilter
import com.intellij.openapi.components.BaseState import com.intellij.openapi.components.BaseState
import com.intellij.openapi.components.SettingsCategory import com.intellij.openapi.components.SettingsCategory
import com.intellij.openapi.components.SimplePersistentStateComponent import com.intellij.openapi.components.SimplePersistentStateComponent
@ -38,7 +38,7 @@ class LensSettingsState : SimplePersistentStateComponent<LensSettingsState.State
fun update() { fun update() {
severityFilter = createSeverityFilter() severityFilter = createSeverityFilter()
InspectionLens.scheduleRefresh() InspectionLensRefresher.scheduleRefresh()
} }
private fun createSeverityFilter(): LensSeverityFilter { private fun createSeverityFilter(): LensSeverityFilter {

View File

@ -1,5 +1,6 @@
package com.chylex.intellij.inspectionlens.editor.lens package com.chylex.intellij.inspectionlens
import com.chylex.intellij.inspectionlens.editor.EditorLensInlay
import com.intellij.lang.annotation.HighlightSeverity import com.intellij.lang.annotation.HighlightSeverity
import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Nested
@ -47,7 +48,7 @@ class EditorLensTest {
} }
/** /**
* If any of these changes, re-evaluate [EditorLensInlay.MAXIMUM_SEVERITY] and the priority calculations. * If any of these change, re-evaluate [EditorLensInlay.MAXIMUM_SEVERITY] and the priority calculations.
*/ */
@Nested @Nested
inner class IdeaHighlightSeverityAssumptions { inner class IdeaHighlightSeverityAssumptions {