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()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
testImplementation("org.junit.jupiter:junit-jupiter:5.9.0")
|
||||||
|
}
|
||||||
|
|
||||||
intellij {
|
intellij {
|
||||||
version.set("2022.2")
|
version.set("2022.2")
|
||||||
updateSinceUntilBuild.set(false)
|
updateSinceUntilBuild.set(false)
|
||||||
@ -27,6 +31,10 @@ tasks.buildSearchableOptions {
|
|||||||
enabled = false
|
enabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
|
|
||||||
tasks.withType<KotlinCompile> {
|
tasks.withType<KotlinCompile> {
|
||||||
kotlinOptions.jvmTarget = "11"
|
kotlinOptions.jvmTarget = "11"
|
||||||
kotlinOptions.freeCompilerArgs = listOf(
|
kotlinOptions.freeCompilerArgs = listOf(
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.chylex.intellij.inspectionlens
|
package com.chylex.intellij.inspectionlens
|
||||||
|
|
||||||
|
import com.chylex.intellij.inspectionlens.EditorInlayLensManager.Companion.MAXIMUM_SEVERITY
|
||||||
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
|
||||||
import com.intellij.openapi.editor.Inlay
|
import com.intellij.openapi.editor.Inlay
|
||||||
@ -14,6 +15,13 @@ class EditorInlayLensManager private constructor(private val editor: Editor) {
|
|||||||
companion object {
|
companion object {
|
||||||
private val KEY = Key<EditorInlayLensManager>(EditorInlayLensManager::class.java.name)
|
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 {
|
fun getOrCreate(editor: Editor): EditorInlayLensManager {
|
||||||
return editor.getUserData(KEY) ?: EditorInlayLensManager(editor).also { editor.putUserData(KEY, it) }
|
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.
|
// Ensures a highlight at the end of a line does not overflow to the next line.
|
||||||
return info.actualEndOffset - 1
|
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>>()
|
private val inlays = mutableMapOf<RangeHighlighter, Inlay<LensRenderer>>()
|
||||||
@ -43,8 +64,9 @@ class EditorInlayLensManager private constructor(private val editor: Editor) {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val offset = getInlayHintOffset(info)
|
val offset = getInlayHintOffset(info)
|
||||||
|
val priority = getInlayHintPriority(info)
|
||||||
val renderer = LensRenderer(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 {
|
editor.inlayModel.addAfterLineEndElement(offset, properties, renderer)?.let {
|
||||||
inlays[highlighter] = it
|
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 val ATTRIBUTES_SINGLETON = TextAttributes(null, null, null, null, Font.ITALIC)
|
||||||
|
|
||||||
private fun getValidDescriptionText(text: String?): String {
|
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 {
|
private fun addMissingPeriod(text: String): String {
|
||||||
return if (text.endsWith('.')) text else "$text."
|
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) {
|
private fun fixBaselineForTextRendering(rect: Rectangle) {
|
||||||
rect.y += 1
|
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