1
0
mirror of https://github.com/chylex/IntelliJ-Inspection-Lens.git synced 2025-05-06 05:34:06 +02:00

Rewrite inlay hint priority calculation to use position in line instead of the whole document

This commit is contained in:
chylex 2023-01-07 05:25:38 +01:00
parent 0aff0f49ae
commit ca4fb0484a
Signed by: chylex
GPG Key ID: 4DE42C8F19A80548
2 changed files with 33 additions and 21 deletions
src
main/kotlin/com/chylex/intellij/inspectionlens
test/kotlin/com/chylex/intellij/inspectionlens

View File

@ -1,6 +1,5 @@
package com.chylex.intellij.inspectionlens
import com.chylex.intellij.inspectionlens.EditorInlayLensManager.Companion.MAXIMUM_SEVERITY
import com.intellij.codeInsight.daemon.impl.HighlightInfo
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.Inlay
@ -17,10 +16,10 @@ class EditorInlayLensManager private constructor(private val editor: Editor) {
/**
* Highest allowed severity for the purposes of sorting multiple highlights at the same offset.
* A [MAXIMUM_SEVERITY] of 500 allows for 8 589 933 positions in the document before sorting breaks down.
* 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(KEY) ?: EditorInlayLensManager(editor).also { editor.putUserData(KEY, it) }
@ -39,18 +38,14 @@ class EditorInlayLensManager private constructor(private val editor: Editor) {
return info.actualEndOffset - 1
}
internal fun getInlayHintPriority(offset: Int, severity: Int): Int {
// Sorts highlights first by offset in the document, then by severity.
val positionBucket = offset.coerceAtLeast(0) * MAXIMUM_SEVERITY.toLong()
val positionFactor = (Int.MAX_VALUE - MAXIMUM_SEVERITY - positionBucket).coerceAtLeast(Int.MIN_VALUE + 1L).toInt()
val severityFactor = severity.coerceIn(0, MAXIMUM_SEVERITY)
// The result is between (Int.MIN_VALUE + 1)..Int.MAX_VALUE, allowing for negation without overflow.
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 fun getInlayHintPriority(info: HighlightInfo): Int {
return getInlayHintPriority(info.actualEndOffset, info.severity.myVal)
}
}
private val inlays = mutableMapOf<RangeHighlighter, Inlay<LensRenderer>>()
@ -87,6 +82,17 @@ class EditorInlayLensManager private constructor(private val editor: Editor) {
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)
}

View File

@ -10,16 +10,16 @@ import org.junit.jupiter.params.provider.ValueSource
class EditorInlayLensManagerTest {
@Nested
inner class Priority {
@ParameterizedTest(name = "offsetAndSeverity = {0}")
@ParameterizedTest(name = "positionAndSeverity = {0}")
@ValueSource(ints = [0, -1, Int.MIN_VALUE])
fun minimumOffset(offsetAndSeverity: Int) {
assertEquals(Int.MAX_VALUE, EditorInlayLensManager.getInlayHintPriority(offsetAndSeverity, Int.MAX_VALUE))
fun minimumOffset(positionAndSeverity: Int) {
assertEquals(Int.MAX_VALUE, EditorInlayLensManager.getInlayHintPriority(positionAndSeverity, Int.MAX_VALUE))
}
@ParameterizedTest(name = "offset = {0}")
@ValueSource(ints = [8_589_934, Int.MAX_VALUE])
fun maximumOffset(offset: Int) {
assertEquals(Int.MIN_VALUE + 1, EditorInlayLensManager.getInlayHintPriority(offset, Int.MIN_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))
}
@ParameterizedTest(name = "severity = {0}")
@ -34,17 +34,23 @@ class EditorInlayLensManagerTest {
assertEquals(Int.MAX_VALUE - 1000 + severity, EditorInlayLensManager.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))
}
@ParameterizedTest(name = "severity = {0}")
@ValueSource(ints = [0, 1, 250, 499, 500])
fun penultimatePriorityBucket(severity: Int) {
assertEquals(Int.MIN_VALUE + 295 + severity, EditorInlayLensManager.getInlayHintPriority(8_589_933, severity))
assertEquals(Int.MIN_VALUE + 295 + 500 + severity, EditorInlayLensManager.getInlayHintPriority(8_589_932, severity))
}
/**
* If any of these change, re-evaluate [EditorInlayLensManager.MAXIMUM_SEVERITY] and the priority calculations.
*/
@Nested
inner class IdeaAssumptions {
inner class IdeaHighlightSeverityAssumptions {
@Test
fun smallestSeverityHasNotChanged() {
assertEquals(10, HighlightSeverity.DEFAULT_SEVERITIES.minOf(HighlightSeverity::myVal))