diff --git a/src/main/kotlin/com/chylex/intellij/inspectionlens/InspectionLens.kt b/src/main/kotlin/com/chylex/intellij/inspectionlens/InspectionLens.kt
index a6abbd8..6f2881a 100644
--- a/src/main/kotlin/com/chylex/intellij/inspectionlens/InspectionLens.kt
+++ b/src/main/kotlin/com/chylex/intellij/inspectionlens/InspectionLens.kt
@@ -1,6 +1,6 @@
 package com.chylex.intellij.inspectionlens
 
-import com.chylex.intellij.inspectionlens.editor.EditorInlayLensManager
+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
@@ -36,7 +36,7 @@ internal object InspectionLens {
 	 */
 	fun uninstall() {
 		forEachOpenEditor {
-			EditorInlayLensManager.remove(it.editor)
+			EditorLensManager.remove(it.editor)
 		}
 	}
 	
diff --git a/src/main/kotlin/com/chylex/intellij/inspectionlens/editor/EditorInlayLensManager.kt b/src/main/kotlin/com/chylex/intellij/inspectionlens/editor/EditorInlayLensManager.kt
deleted file mode 100644
index 660055a..0000000
--- a/src/main/kotlin/com/chylex/intellij/inspectionlens/editor/EditorInlayLensManager.kt
+++ /dev/null
@@ -1,105 +0,0 @@
-package com.chylex.intellij.inspectionlens.editor
-
-import com.intellij.codeInsight.daemon.impl.HighlightInfo
-import com.intellij.openapi.editor.Editor
-import com.intellij.openapi.editor.Inlay
-import com.intellij.openapi.editor.InlayProperties
-import com.intellij.openapi.editor.markup.RangeHighlighter
-import com.intellij.openapi.util.Key
-
-/**
- * Manages visible inspection lenses for an [Editor].
- */
-class EditorInlayLensManager private constructor(private val editor: Editor) {
-	companion object {
-		private val EDITOR_KEY = Key<EditorInlayLensManager>(EditorInlayLensManager::class.java.name)
-		
-		/**
-		 * Highest allowed severity for the purposes of sorting multiple highlights at the same offset.
-		 * The value is a little higher than the highest [com.intellij.lang.annotation.HighlightSeverity], in case severities with higher values are introduced in the future.
-		 */
-		private const val MAXIMUM_SEVERITY = 500
-		private const val MAXIMUM_POSITION = ((Int.MAX_VALUE / MAXIMUM_SEVERITY) * 2) - 1
-		
-		fun getOrCreate(editor: Editor): EditorInlayLensManager {
-			return editor.getUserData(EDITOR_KEY) ?: EditorInlayLensManager(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 fun getInlayHintOffset(info: HighlightInfo): Int {
-			// Ensures a highlight at the end of a line does not overflow to the next line.
-			return info.actualEndOffset - 1
-		}
-		
-		internal fun getInlayHintPriority(position: Int, severity: Int): Int {
-			// Sorts highlights first by position on the line, then by severity.
-			val positionBucket = position.coerceIn(0, MAXIMUM_POSITION) * MAXIMUM_SEVERITY
-			// The multiplication can overflow, but subtracting overflowed result from Int.MAX_VALUE does not break continuity.
-			val positionFactor = Integer.MAX_VALUE - positionBucket
-			val severityFactor = severity.coerceIn(0, MAXIMUM_SEVERITY) - MAXIMUM_SEVERITY
-			return positionFactor + severityFactor
-		}
-	}
-	
-	private val inlays = mutableMapOf<RangeHighlighter, Inlay<LensRenderer>>()
-	
-	private fun show(highlighterWithInfo: HighlighterWithInfo) {
-		val (highlighter, info) = highlighterWithInfo
-		val currentInlay = inlays[highlighter]
-		if (currentInlay != null && currentInlay.isValid) {
-			currentInlay.renderer.setPropertiesFrom(info)
-			currentInlay.update()
-		}
-		else {
-			val offset = getInlayHintOffset(info)
-			val priority = getInlayHintPriority(info)
-			val renderer = LensRenderer(info)
-			val properties = InlayProperties().relatesToPrecedingText(true).disableSoftWrapping(true).priority(priority)
-			
-			editor.inlayModel.addAfterLineEndElement(offset, properties, renderer)?.let {
-				inlays[highlighter] = it
-			}
-		}
-	}
-	
-	fun showAll(highlightersWithInfo: Collection<HighlighterWithInfo>) {
-		executeInInlayBatchMode(highlightersWithInfo.size) { highlightersWithInfo.forEach(::show) }
-	}
-	
-	private fun hide(highlighter: RangeHighlighter) {
-		inlays.remove(highlighter)?.dispose()
-	}
-	
-	fun hideAll(highlighters: Collection<RangeHighlighter>) {
-		executeInInlayBatchMode(highlighters.size) { highlighters.forEach(::hide) }
-	}
-	
-	fun hideAll() {
-		if (inlays.isNotEmpty()) {
-			executeInInlayBatchMode(inlays.size) { inlays.values.forEach(Inlay<*>::dispose) }
-			inlays.clear()
-		}
-	}
-	
-	private fun getInlayHintPriority(info: HighlightInfo): Int {
-		val startOffset = info.actualStartOffset
-		val positionOnLine = startOffset - getLineStartOffset(startOffset)
-		return getInlayHintPriority(positionOnLine, info.severity.myVal)
-	}
-	
-	private fun getLineStartOffset(offset: Int): Int {
-		val position = editor.offsetToLogicalPosition(offset)
-		return editor.document.getLineStartOffset(position.line)
-	}
-	
-	private fun executeInInlayBatchMode(operations: Int, block: () -> Unit) {
-		editor.inlayModel.execute(operations > 1000, block)
-	}
-}
diff --git a/src/main/kotlin/com/chylex/intellij/inspectionlens/editor/EditorLens.kt b/src/main/kotlin/com/chylex/intellij/inspectionlens/editor/EditorLens.kt
new file mode 100644
index 0000000..433dd35
--- /dev/null
+++ b/src/main/kotlin/com/chylex/intellij/inspectionlens/editor/EditorLens.kt
@@ -0,0 +1,26 @@
+package com.chylex.intellij.inspectionlens.editor
+
+import com.intellij.codeInsight.daemon.impl.HighlightInfo
+import com.intellij.openapi.editor.Editor
+
+internal class EditorLens private constructor(private var inlay: EditorLensInlay) {
+	fun update(info: HighlightInfo): Boolean {
+		val editor = inlay.editor
+		
+		if (!inlay.tryUpdate(info)) {
+			inlay = EditorLensInlay.show(editor, info) ?: return false
+		}
+		
+		return true
+	}
+	
+	fun hide() {
+		inlay.hide()
+	}
+	
+	companion object {
+		fun show(editor: Editor, info: HighlightInfo): EditorLens? {
+			return EditorLensInlay.show(editor, info)?.let(::EditorLens)
+		}
+	}
+}
diff --git a/src/main/kotlin/com/chylex/intellij/inspectionlens/editor/EditorLensInlay.kt b/src/main/kotlin/com/chylex/intellij/inspectionlens/editor/EditorLensInlay.kt
new file mode 100644
index 0000000..3adb149
--- /dev/null
+++ b/src/main/kotlin/com/chylex/intellij/inspectionlens/editor/EditorLensInlay.kt
@@ -0,0 +1,70 @@
+package com.chylex.intellij.inspectionlens.editor
+
+import com.intellij.codeInsight.daemon.impl.HighlightInfo
+import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.editor.Inlay
+import com.intellij.openapi.editor.InlayProperties
+
+@JvmInline
+internal value class EditorLensInlay(private val inlay: Inlay<LensRenderer>) {
+	val editor
+		get() = inlay.editor
+	
+	fun tryUpdate(info: HighlightInfo): Boolean {
+		if (!inlay.isValid) {
+			return false
+		}
+		
+		inlay.renderer.setPropertiesFrom(info)
+		inlay.update()
+		return true
+	}
+	
+	fun hide() {
+		inlay.dispose()
+	}
+	
+	companion object {
+		fun show(editor: Editor, info: HighlightInfo): EditorLensInlay? {
+			val offset = getInlayHintOffset(info)
+			val priority = getInlayHintPriority(editor, info)
+			
+			val renderer = LensRenderer(info)
+			val properties = InlayProperties().relatesToPrecedingText(true).disableSoftWrapping(true).priority(priority)
+			
+			return editor.inlayModel.addAfterLineEndElement(offset, properties, renderer)?.let(::EditorLensInlay)
+		}
+		
+		/**
+		 * Highest allowed severity for the purposes of sorting multiple highlights at the same offset.
+		 * The value is a little higher than the highest [com.intellij.lang.annotation.HighlightSeverity], in case severities with higher values are introduced in the future.
+		 */
+		private const val MAXIMUM_SEVERITY = 500
+		private const val MAXIMUM_POSITION = ((Int.MAX_VALUE / MAXIMUM_SEVERITY) * 2) - 1
+		
+		private fun getInlayHintOffset(info: HighlightInfo): Int {
+			// Ensures a highlight at the end of a line does not overflow to the next line.
+			return info.actualEndOffset - 1
+		}
+		
+		fun getInlayHintPriority(position: Int, severity: Int): Int {
+			// Sorts highlights first by position on the line, then by severity.
+			val positionBucket = position.coerceIn(0, MAXIMUM_POSITION) * MAXIMUM_SEVERITY
+			// The multiplication can overflow, but subtracting overflowed result from Int.MAX_VALUE does not break continuity.
+			val positionFactor = Integer.MAX_VALUE - positionBucket
+			val severityFactor = severity.coerceIn(0, MAXIMUM_SEVERITY) - MAXIMUM_SEVERITY
+			return positionFactor + severityFactor
+		}
+		
+		private fun getInlayHintPriority(editor: Editor, info: HighlightInfo): Int {
+			val startOffset = info.actualStartOffset
+			val positionOnLine = startOffset - getLineStartOffset(editor, startOffset)
+			return getInlayHintPriority(positionOnLine, info.severity.myVal)
+		}
+		
+		private fun getLineStartOffset(editor: Editor, offset: Int): Int {
+			val position = editor.offsetToLogicalPosition(offset)
+			return editor.document.getLineStartOffset(position.line)
+		}	
+	}
+}
diff --git a/src/main/kotlin/com/chylex/intellij/inspectionlens/editor/EditorLensManager.kt b/src/main/kotlin/com/chylex/intellij/inspectionlens/editor/EditorLensManager.kt
new file mode 100644
index 0000000..99f48ef
--- /dev/null
+++ b/src/main/kotlin/com/chylex/intellij/inspectionlens/editor/EditorLensManager.kt
@@ -0,0 +1,83 @@
+package com.chylex.intellij.inspectionlens.editor
+
+import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.editor.markup.RangeHighlighter
+import com.intellij.openapi.util.Key
+import java.util.IdentityHashMap
+
+/**
+ * Manages visible inspection lenses for an [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 fun show(highlighterWithInfo: HighlighterWithInfo) {
+		val (highlighter, info) = highlighterWithInfo
+		
+		val existingLens = lenses[highlighter]
+		if (existingLens != null) {
+			if (existingLens.update(info)) {
+				return
+			}
+			
+			existingLens.hide()
+		}
+		
+		val newLens = EditorLens.show(editor, info)
+		if (newLens != null) {
+			lenses[highlighter] = newLens
+		}
+		else if (existingLens != null) {
+			lenses.remove(highlighter)
+		}
+	}
+	
+	fun show(highlightersWithInfo: Collection<HighlighterWithInfo>) {
+		executeInBatchMode(highlightersWithInfo.size) {
+			highlightersWithInfo.forEach(::show)
+		}
+	}
+	
+	private fun hide(highlighter: RangeHighlighter) {
+		lenses.remove(highlighter)?.hide()
+	}
+	
+	fun hide(highlighters: Collection<RangeHighlighter>) {
+		executeInBatchMode(highlighters.size) {
+			highlighters.forEach(::hide)
+		}
+	}
+	
+	fun hideAll() {
+		executeInBatchMode(lenses.size) {
+			lenses.values.forEach(EditorLens::hide)
+			lenses.clear()
+		}
+	}
+	
+	@Suppress("ConvertLambdaToReference")
+	private inline fun executeInBatchMode(operations: Int, crossinline action: () -> Unit) {
+		if (operations > 1000) {
+			editor.inlayModel.execute(true) { action() }
+		}
+		else {
+			action()
+		}
+	}
+}
diff --git a/src/main/kotlin/com/chylex/intellij/inspectionlens/editor/LensMarkupModelListener.kt b/src/main/kotlin/com/chylex/intellij/inspectionlens/editor/LensMarkupModelListener.kt
index c9a91df..b368f59 100644
--- a/src/main/kotlin/com/chylex/intellij/inspectionlens/editor/LensMarkupModelListener.kt
+++ b/src/main/kotlin/com/chylex/intellij/inspectionlens/editor/LensMarkupModelListener.kt
@@ -14,13 +14,13 @@ import com.intellij.openapi.util.Disposer
 import com.intellij.openapi.util.Key
 
 /**
- * Listens for inspection highlights and reports them to [EditorInlayLensManager].
+ * Listens for inspection highlights and reports them to [EditorLensManager].
  */
 internal class LensMarkupModelListener private constructor(editor: Editor) : MarkupModelListener {
-	private val lens = EditorInlayLensManager.getOrCreate(editor)
+	private val lensManager = EditorLensManager.getOrCreate(editor)
 	
-	private val showOnDispatchThread = DebouncingInvokeOnDispatchThread(lens::showAll)
-	private val hideOnDispatchThread = DebouncingInvokeOnDispatchThread(lens::hideAll)
+	private val showOnDispatchThread = DebouncingInvokeOnDispatchThread(lensManager::show)
+	private val hideOnDispatchThread = DebouncingInvokeOnDispatchThread(lensManager::hide)
 	
 	override fun afterAdded(highlighter: RangeHighlighterEx) {
 		showIfValid(highlighter)
@@ -81,7 +81,7 @@ internal class LensMarkupModelListener private constructor(editor: Editor) : Mar
 		}
 		
 		/**
-		 * Attaches a new [LensMarkupModelListener] to the [Editor], and reports all existing inspection highlights to [EditorInlayLensManager].
+		 * 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) {
diff --git a/src/test/kotlin/com/chylex/intellij/inspectionlens/EditorInlayLensManagerTest.kt b/src/test/kotlin/com/chylex/intellij/inspectionlens/EditorLensTest.kt
similarity index 64%
rename from src/test/kotlin/com/chylex/intellij/inspectionlens/EditorInlayLensManagerTest.kt
rename to src/test/kotlin/com/chylex/intellij/inspectionlens/EditorLensTest.kt
index 5915aff..dbfc6e0 100644
--- a/src/test/kotlin/com/chylex/intellij/inspectionlens/EditorInlayLensManagerTest.kt
+++ b/src/test/kotlin/com/chylex/intellij/inspectionlens/EditorLensTest.kt
@@ -1,6 +1,6 @@
 package com.chylex.intellij.inspectionlens
 
-import com.chylex.intellij.inspectionlens.editor.EditorInlayLensManager
+import com.chylex.intellij.inspectionlens.editor.EditorLensInlay
 import com.intellij.lang.annotation.HighlightSeverity
 import org.junit.jupiter.api.Assertions.assertEquals
 import org.junit.jupiter.api.Nested
@@ -8,47 +8,47 @@ import org.junit.jupiter.api.Test
 import org.junit.jupiter.params.ParameterizedTest
 import org.junit.jupiter.params.provider.ValueSource
 
-class EditorInlayLensManagerTest {
+class EditorLensTest {
 	@Nested
 	inner class Priority {
 		@ParameterizedTest(name = "positionAndSeverity = {0}")
 		@ValueSource(ints = [0, -1, Int.MIN_VALUE])
 		fun minimumOffset(positionAndSeverity: Int) {
-			assertEquals(Int.MAX_VALUE, EditorInlayLensManager.getInlayHintPriority(positionAndSeverity, Int.MAX_VALUE))
+			assertEquals(Int.MAX_VALUE, EditorLensInlay.getInlayHintPriority(positionAndSeverity, Int.MAX_VALUE))
 		}
 		
 		@ParameterizedTest(name = "position = {0}")
 		@ValueSource(ints = [8_589_933, Int.MAX_VALUE])
 		fun maximumOffset(position: Int) {
-			assertEquals(Int.MIN_VALUE + 295, EditorInlayLensManager.getInlayHintPriority(position, Int.MIN_VALUE))
+			assertEquals(Int.MIN_VALUE + 295, EditorLensInlay.getInlayHintPriority(position, Int.MIN_VALUE))
 		}
 		
 		@ParameterizedTest(name = "severity = {0}")
 		@ValueSource(ints = [0, 1, 250, 499, 500])
 		fun firstPriorityBucket(severity: Int) {
-			assertEquals(Int.MAX_VALUE - 500 + severity, EditorInlayLensManager.getInlayHintPriority(0, severity))
+			assertEquals(Int.MAX_VALUE - 500 + severity, EditorLensInlay.getInlayHintPriority(0, severity))
 		}
 		
 		@ParameterizedTest(name = "severity = {0}")
 		@ValueSource(ints = [0, 1, 250, 499, 500])
 		fun secondPriorityBucket(severity: Int) {
-			assertEquals(Int.MAX_VALUE - 1000 + severity, EditorInlayLensManager.getInlayHintPriority(1, severity))
+			assertEquals(Int.MAX_VALUE - 1000 + severity, EditorLensInlay.getInlayHintPriority(1, severity))
 		}
 		
 		@ParameterizedTest(name = "severity = {0}")
 		@ValueSource(ints = [0, 1, 250, 499, 500])
 		fun middlePriorityBucket(severity: Int) {
-			assertEquals(-353 + severity, EditorInlayLensManager.getInlayHintPriority(4294967, severity))
+			assertEquals(-353 + severity, EditorLensInlay.getInlayHintPriority(4294967, severity))
 		}
 		
 		@ParameterizedTest(name = "severity = {0}")
 		@ValueSource(ints = [0, 1, 250, 499, 500])
 		fun penultimatePriorityBucket(severity: Int) {
-			assertEquals(Int.MIN_VALUE + 295 + 500 + severity, EditorInlayLensManager.getInlayHintPriority(8_589_932, severity))
+			assertEquals(Int.MIN_VALUE + 295 + 500 + severity, EditorLensInlay.getInlayHintPriority(8_589_932, severity))
 		}
 		
 		/**
-		 * If any of these change, re-evaluate [EditorInlayLensManager.MAXIMUM_SEVERITY] and the priority calculations.
+		 * If any of these change, re-evaluate [EditorLensInlay.MAXIMUM_SEVERITY] and the priority calculations.
 		 */
 		@Nested
 		inner class IdeaHighlightSeverityAssumptions {