mirror of
https://github.com/chylex/IntelliJ-Inspection-Lens.git
synced 2024-11-25 16:42:54 +01:00
Compare commits
2 Commits
7c3910854d
...
0aff0f49ae
Author | SHA1 | Date | |
---|---|---|---|
0aff0f49ae | |||
e2384a98a8 |
@ -14,6 +14,10 @@ repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.9.0")
|
||||
}
|
||||
|
||||
intellij {
|
||||
version.set("2022.2")
|
||||
updateSinceUntilBuild.set(false)
|
||||
@ -27,6 +31,10 @@ tasks.buildSearchableOptions {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
kotlinOptions.jvmTarget = "11"
|
||||
kotlinOptions.freeCompilerArgs = listOf(
|
||||
|
@ -1,5 +1,6 @@
|
||||
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
|
||||
@ -14,6 +15,13 @@ class EditorInlayLensManager private constructor(private val editor: Editor) {
|
||||
companion object {
|
||||
private val KEY = Key<EditorInlayLensManager>(EditorInlayLensManager::class.java.name)
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
fun getOrCreate(editor: Editor): EditorInlayLensManager {
|
||||
return editor.getUserData(KEY) ?: EditorInlayLensManager(editor).also { editor.putUserData(KEY, it) }
|
||||
}
|
||||
@ -30,6 +38,19 @@ class EditorInlayLensManager private constructor(private val editor: Editor) {
|
||||
// Ensures a highlight at the end of a line does not overflow to the next line.
|
||||
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.
|
||||
return positionFactor + severityFactor
|
||||
}
|
||||
|
||||
private fun getInlayHintPriority(info: HighlightInfo): Int {
|
||||
return getInlayHintPriority(info.actualEndOffset, info.severity.myVal)
|
||||
}
|
||||
}
|
||||
|
||||
private val inlays = mutableMapOf<RangeHighlighter, Inlay<LensRenderer>>()
|
||||
@ -43,8 +64,9 @@ class EditorInlayLensManager private constructor(private val editor: Editor) {
|
||||
}
|
||||
else {
|
||||
val offset = getInlayHintOffset(info)
|
||||
val priority = getInlayHintPriority(info)
|
||||
val renderer = LensRenderer(info)
|
||||
val properties = InlayProperties().relatesToPrecedingText(true).disableSoftWrapping(true).priority(-offset)
|
||||
val properties = InlayProperties().relatesToPrecedingText(true).disableSoftWrapping(true).priority(priority)
|
||||
|
||||
editor.inlayModel.addAfterLineEndElement(offset, properties, renderer)?.let {
|
||||
inlays[highlighter] = it
|
||||
|
@ -42,13 +42,23 @@ class LensRenderer(info: HighlightInfo) : HintRenderer(null) {
|
||||
private val ATTRIBUTES_SINGLETON = TextAttributes(null, null, null, null, Font.ITALIC)
|
||||
|
||||
private fun getValidDescriptionText(text: String?): String {
|
||||
return if (text.isNullOrBlank()) " " else addMissingPeriod(StringUtil.unescapeXmlEntities(StringUtil.stripHtml(text, " ")))
|
||||
return if (text.isNullOrBlank()) " " else addMissingPeriod(convertHtmlToText(text))
|
||||
}
|
||||
|
||||
private fun convertHtmlToText(potentialHtml: String): String {
|
||||
return potentialHtml
|
||||
.ifContains('<') { StringUtil.stripHtml(it, " ") }
|
||||
.ifContains('&', StringUtil::unescapeXmlEntities)
|
||||
}
|
||||
|
||||
private fun addMissingPeriod(text: String): String {
|
||||
return if (text.endsWith('.')) text else "$text."
|
||||
}
|
||||
|
||||
private inline fun String.ifContains(charToTest: Char, action: (String) -> String): String {
|
||||
return if (this.contains(charToTest)) action(this) else this
|
||||
}
|
||||
|
||||
private fun fixBaselineForTextRendering(rect: Rectangle) {
|
||||
rect.y += 1
|
||||
}
|
||||
|
@ -0,0 +1,59 @@
|
||||
package com.chylex.intellij.inspectionlens
|
||||
|
||||
import com.intellij.lang.annotation.HighlightSeverity
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.params.ParameterizedTest
|
||||
import org.junit.jupiter.params.provider.ValueSource
|
||||
|
||||
class EditorInlayLensManagerTest {
|
||||
@Nested
|
||||
inner class Priority {
|
||||
@ParameterizedTest(name = "offsetAndSeverity = {0}")
|
||||
@ValueSource(ints = [0, -1, Int.MIN_VALUE])
|
||||
fun minimumOffset(offsetAndSeverity: Int) {
|
||||
assertEquals(Int.MAX_VALUE, EditorInlayLensManager.getInlayHintPriority(offsetAndSeverity, 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 = "severity = {0}")
|
||||
@ValueSource(ints = [0, 1, 250, 499, 500])
|
||||
fun firstPriorityBucket(severity: Int) {
|
||||
assertEquals(Int.MAX_VALUE - 500 + severity, EditorInlayLensManager.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))
|
||||
}
|
||||
|
||||
@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))
|
||||
}
|
||||
|
||||
/**
|
||||
* If any of these change, re-evaluate [EditorInlayLensManager.MAXIMUM_SEVERITY] and the priority calculations.
|
||||
*/
|
||||
@Nested
|
||||
inner class IdeaAssumptions {
|
||||
@Test
|
||||
fun smallestSeverityHasNotChanged() {
|
||||
assertEquals(10, HighlightSeverity.DEFAULT_SEVERITIES.minOf(HighlightSeverity::myVal))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun highestSeverityHasNotChanged() {
|
||||
assertEquals(400, HighlightSeverity.DEFAULT_SEVERITIES.maxOf(HighlightSeverity::myVal))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user