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

Compare commits

...

2 Commits

Author SHA1 Message Date
632a052ff9
Refactor code and organize packages 2024-12-24 06:39:45 +01:00
f2ec3c3d9b
Add dictionary file for IntelliJ 2024-12-24 05:43:41 +01:00
22 changed files with 129 additions and 145 deletions

1
.gitignore vendored
View File

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

View File

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

View File

@ -1,15 +1,11 @@
package com.chylex.intellij.inspectionlens package com.chylex.intellij.inspectionlens
import com.chylex.intellij.inspectionlens.editor.EditorLensManager import com.chylex.intellij.inspectionlens.editor.EditorLensFeatures
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.
@ -21,7 +17,7 @@ internal object InspectionLens {
* Installs lenses into [editor]. * Installs lenses into [editor].
*/ */
fun install(editor: TextEditor) { fun install(editor: TextEditor) {
LensMarkupModelListener.register(editor.editor, createEditorDisposable(editor)) EditorLensFeatures.install(editor.editor, service<InspectionLensPluginDisposableService>().intersect(editor))
} }
/** /**
@ -31,31 +27,20 @@ internal object InspectionLens {
forEachOpenEditor(::install) forEachOpenEditor(::install)
} }
/**
* Uninstalls lenses from all open editors.
*/
fun uninstall() {
forEachOpenEditor {
EditorLensManager.remove(it.editor)
}
}
/** /**
* Refreshes lenses in all open editors. * Refreshes lenses in all open editors.
*/ */
fun refresh() { private fun refresh() {
forEachOpenEditor { forEachOpenEditor {
LensMarkupModelListener.refresh(it.editor) EditorLensFeatures.refresh(it.editor)
} }
} }
/** /**
* Creates a [Disposable] that will be disposed when either the [TextEditor] is disposed or the plugin is unloaded. * Schedules a refresh of lenses in all open editors.
*/ */
private fun createEditorDisposable(textEditor: TextEditor): Disposable { fun scheduleRefresh() {
val pluginLifetime = ApplicationManager.getApplication().getService(InspectionLensPluginDisposableService::class.java).createLifetime() Refresh.schedule()
val editorLifetime = textEditor.createLifetime()
return Lifetime.intersect(pluginLifetime, editorLifetime).createNestedDisposable("InspectionLensIntersectedLifetime")
} }
/** /**
@ -70,4 +55,26 @@ 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,11 +2,21 @@ 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, and uninstalls it when the plugin is unloaded. * Installs [InspectionLens] in open editors when the plugin is loaded.
*/ */
class InspectionLensPluginListener : DynamicPluginListener { class InspectionLensPluginListener : DynamicPluginListener {
override fun pluginLoaded(pluginDescriptor: IdeaPluginDescriptor) { override fun pluginLoaded(pluginDescriptor: IdeaPluginDescriptor) {
@ -12,10 +12,4 @@ 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

@ -1,25 +0,0 @@
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.LensSeverity import com.chylex.intellij.inspectionlens.editor.lens.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.LensSeverity import com.chylex.intellij.inspectionlens.editor.lens.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
object SpellCheckerSupport { internal object SpellCheckerSupport {
private val log = logger<SpellCheckerSupport>() private val log = logger<SpellCheckerSupport>()
fun load() { fun load() {

View File

@ -0,0 +1,51 @@
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,32 +1,16 @@
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].
*/ */
class EditorLensManager private constructor(private val editor: Editor) { internal class EditorLensManager(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
class EditorLensManagerDispatcher(private val lensManager: EditorLensManager) { internal 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,23 +2,16 @@ 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 constructor(editor: Editor) : MarkupModelListener { internal class LensMarkupModelListener(private val lensManagerDispatcher: EditorLensManagerDispatcher) : 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)
@ -46,11 +39,11 @@ internal class LensMarkupModelListener private constructor(editor: Editor) : Mar
} }
} }
private fun showAllValid(highlighters: Array<RangeHighlighter>) { fun showAllValid(highlighters: Array<RangeHighlighter>) {
highlighters.forEach(::showIfValid) highlighters.forEach(::showIfValid)
} }
private fun hideAll() { fun hideAll() {
lensManagerDispatcher.hideAll() lensManagerDispatcher.hideAll()
} }
@ -65,49 +58,12 @@ internal class LensMarkupModelListener private constructor(editor: Editor) : Mar
} }
} }
companion object { private inline fun processHighlighterWithInfo(highlighterWithInfo: HighlighterWithInfo, actionForImmediate: (HighlighterWithInfo) -> Unit, actionForAsync: (HighlighterWithInfo.Async) -> Unit) {
private val EDITOR_KEY = Key<LensMarkupModelListener>(LensMarkupModelListener::class.java.name) if (highlighterWithInfo is HighlighterWithInfo.Async) {
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) {
private fun getMarkupModel(editor: Editor): MarkupModelEx? { actionForImmediate(highlighterWithInfo)
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 package com.chylex.intellij.inspectionlens.editor.lens
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 package com.chylex.intellij.inspectionlens.editor.lens
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 package com.chylex.intellij.inspectionlens.editor.lens
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,4 +1,4 @@
package com.chylex.intellij.inspectionlens.editor package com.chylex.intellij.inspectionlens.editor.lens
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 package com.chylex.intellij.inspectionlens.editor.lens
import com.chylex.intellij.inspectionlens.InspectionLensRefresher import com.chylex.intellij.inspectionlens.InspectionLens
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) {
InspectionLensRefresher.scheduleRefresh() InspectionLens.scheduleRefresh()
} }
} }

View File

@ -1,4 +1,4 @@
package com.chylex.intellij.inspectionlens.editor package com.chylex.intellij.inspectionlens.editor.lens
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 package com.chylex.intellij.inspectionlens.editor.lens
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.LensSeverityFilter import com.chylex.intellij.inspectionlens.editor.lens.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.InspectionLensRefresher import com.chylex.intellij.inspectionlens.InspectionLens
import com.chylex.intellij.inspectionlens.editor.LensSeverityFilter import com.chylex.intellij.inspectionlens.editor.lens.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()
InspectionLensRefresher.scheduleRefresh() InspectionLens.scheduleRefresh()
} }
private fun createSeverityFilter(): LensSeverityFilter { private fun createSeverityFilter(): LensSeverityFilter {

View File

@ -1,6 +1,5 @@
package com.chylex.intellij.inspectionlens package com.chylex.intellij.inspectionlens.editor.lens
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
@ -48,7 +47,7 @@ class EditorLensTest {
} }
/** /**
* If any of these change, re-evaluate [EditorLensInlay.MAXIMUM_SEVERITY] and the priority calculations. * If any of these changes, re-evaluate [EditorLensInlay.MAXIMUM_SEVERITY] and the priority calculations.
*/ */
@Nested @Nested
inner class IdeaHighlightSeverityAssumptions { inner class IdeaHighlightSeverityAssumptions {