@@ -2,26 +2,14 @@ package com.coroutines.androidresourceusagetracker
22
33import com.intellij.codeInsight.daemon.LineMarkerInfo
44import com.intellij.codeInsight.daemon.LineMarkerProvider
5- import com.intellij.openapi.application.ApplicationManager
65import com.intellij.openapi.editor.markup.GutterIconRenderer
7- import com.intellij.openapi.project.Project
8- import com.intellij.openapi.util.Key
96import com.intellij.psi.PsiElement
107import com.intellij.psi.xml.XmlTag
11- import com.intellij.util.concurrency.AppExecutorUtil
12- import java.util.concurrent.ConcurrentHashMap
13- import java.util.concurrent.TimeUnit
148
159class ResourceUsageLineMarkerProvider : LineMarkerProvider {
1610
17- companion object {
18- private val CACHE = ConcurrentHashMap <String , Int >()
19- private val COMPUTING = ConcurrentHashMap <String , Boolean >()
20- private const val CACHE_INVALIDATION_DELAY_MS = 5000L
21- }
22-
2311 override fun getLineMarkerInfo (element : PsiElement ): LineMarkerInfo <* >? {
24- return null // All work happens in collectSlowLineMarkers
12+ return null
2513 }
2614
2715 override fun collectSlowLineMarkers (
@@ -33,78 +21,59 @@ class ResourceUsageLineMarkerProvider : LineMarkerProvider {
3321 if (! isAndroidResourceTag(element)) continue
3422
3523 val resourceName = element.getAttributeValue(" name" ) ? : continue
36- val project = element.project
37- val cacheKey = getCacheKey(project, resourceName)
38-
39- // Get cached count or use placeholder
40- val usageCount = CACHE .getOrDefault(cacheKey, - 1 )
41-
42- // Create icon (use placeholder for -1)
43- val icon = if (usageCount >= 0 ) {
44- ResourceUsageIconGenerator .createIcon(usageCount)
45- } else {
46- ResourceUsageIconGenerator .createLoadingIcon()
47- }
24+ val usageCount = UsageCounter .countUsages(element)
4825
49- // Create line marker
26+ val icon = ResourceUsageIconGenerator .createIcon(usageCount)
5027 val anchorElement = element.firstChild ? : continue
28+
5129 val lineMarker = LineMarkerInfo (
5230 anchorElement,
5331 element.textRange,
5432 icon,
55- { if (usageCount >= 0 ) " Used $ usageCount time ${ if (usageCount != 1 ) " s " else " " } " else " Computing... " },
33+ { createTooltip(resourceName, usageCount, element) },
5634 null ,
5735 GutterIconRenderer .Alignment .RIGHT
5836 )
5937
6038 result.add(lineMarker)
61-
62- // Start async computation if not cached
63- if (usageCount == - 1 && COMPUTING .putIfAbsent(cacheKey, true ) == null ) {
64- scheduleUsageCountComputation(element, resourceName, cacheKey, project)
65- }
6639 }
6740 }
6841
69- private fun scheduleUsageCountComputation (
70- element : XmlTag ,
71- resourceName : String ,
72- cacheKey : String ,
73- project : Project
74- ) {
75- AppExecutorUtil .getAppScheduledExecutorService().schedule({
76- try {
77- val count = UsageCounter .countUsages(element)
78- CACHE [cacheKey] = count
79-
80- // Schedule cache invalidation
81- AppExecutorUtil .getAppScheduledExecutorService().schedule({
82- CACHE .remove(cacheKey)
83- }, CACHE_INVALIDATION_DELAY_MS , TimeUnit .MILLISECONDS )
84-
85- // Trigger UI update
86- ApplicationManager .getApplication().invokeLater {
87- com.intellij.codeInsight.daemon.DaemonCodeAnalyzer
88- .getInstance(project)
89- .restart()
90- }
91- } finally {
92- COMPUTING .remove(cacheKey)
42+ private fun createTooltip (resourceName : String , usageCount : Int , element : XmlTag ): String {
43+ if (usageCount == 0 ) {
44+ return " $resourceName : Not used"
45+ }
46+
47+ val usages = UsageCounter .getUsages(element)
48+ val displayCount = if (usageCount > 99 ) " 99+" else usageCount.toString()
49+
50+ val tooltip = buildString {
51+ append(" <html>" )
52+ append(" <b>$resourceName </b>: $displayCount usage${if (usageCount != 1 ) " s" else " " } <br><br>" )
53+
54+ // Show up to 5 usages in tooltip
55+ usages.take(5 ).forEach { usage ->
56+ append(" <b>${usage.filePath} :${usage.lineNumber} </b><br>" )
57+ append(" <code>${usage.codeSnippet} </code><br><br>" )
9358 }
94- }, 100 , TimeUnit .MILLISECONDS ) // Small delay to batch requests
95- }
9659
97- private fun getCacheKey (project : Project , resourceName : String ): String {
98- return " ${project.name} :$resourceName "
60+ if (usages.size > 5 ) {
61+ append(" <i>...and ${usages.size - 5 } more</i>" )
62+ }
63+
64+ append(" </html>" )
65+ }
66+
67+ return tooltip
9968 }
10069
10170 private fun isAndroidResourceTag (tag : XmlTag ): Boolean {
102- val tagName = tag.name
10371 val validTags = setOf (
10472 " string" , " color" , " dimen" , " style" , " drawable" ,
10573 " integer" , " bool" , " array" , " string-array" , " integer-array" ,
10674 " plurals" , " attr" , " declare-styleable" , " item" , " id"
10775 )
108- return validTags.contains(tagName ) && tag.getAttribute(" name" ) != null
76+ return validTags.contains(tag.name ) && tag.getAttribute(" name" ) != null
10977 }
110- }
78+ }
79+
0 commit comments