11package com.tang.intellij.lua.editor
22
3+ import com.cppcxy.ide.lsp.GutterInfo
34import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer
5+ import com.intellij.openapi.Disposable
46import com.intellij.openapi.application.ApplicationManager
7+ import com.intellij.openapi.components.Service
8+ import com.intellij.openapi.components.Service.Level.PROJECT
9+ import com.intellij.openapi.components.service
510import com.intellij.openapi.editor.Document
11+ import com.intellij.openapi.editor.EditorFactory
612import com.intellij.openapi.editor.event.DocumentEvent
713import com.intellij.openapi.editor.event.DocumentListener
814import com.intellij.openapi.fileEditor.FileDocumentManager
@@ -11,41 +17,45 @@ import com.intellij.openapi.fileEditor.FileEditorManagerListener
1117import com.intellij.openapi.project.Project
1218import com.intellij.openapi.startup.ProjectActivity
1319import com.intellij.openapi.vfs.VirtualFile
20+ import com.intellij.openapi.vfs.VirtualFileManager
21+ import com.intellij.openapi.vfs.newvfs.BulkFileListener
22+ import com.intellij.openapi.vfs.newvfs.events.VFileEvent
1423import com.intellij.psi.PsiManager
24+ import com.tang.intellij.lua.lang.LuaFileType
1525import com.tang.intellij.lua.psi.LuaPsiFile
1626import java.util.concurrent.ConcurrentHashMap
1727
1828/* *
1929 * Manager to handle gutter cache and trigger updates
2030 */
2131object LuaGutterCacheManager {
22- private val gutterCache = ConcurrentHashMap <String , List <com.cppcxy.ide.lsp. GutterInfo >>()
32+ private val gutterCache = ConcurrentHashMap <String , List <GutterInfo >>()
2333 private val cacheTimestamps = ConcurrentHashMap <String , Long >()
24-
34+
2535 fun clearCache (uri : String ) {
2636 gutterCache.remove(uri)
2737 cacheTimestamps.remove(uri)
2838 }
29-
39+
3040 fun clearAllCache () {
3141 gutterCache.clear()
3242 cacheTimestamps.clear()
3343 }
34-
35- fun getCache (uri : String ): List <com.cppcxy.ide.lsp. GutterInfo >? {
44+
45+ fun getCache (uri : String ): List <GutterInfo >? {
3646 return gutterCache[uri]
3747 }
38-
39- fun setCache (uri : String , infos : List <com.cppcxy.ide.lsp. GutterInfo >) {
48+
49+ fun setCache (uri : String , infos : List <GutterInfo >) {
4050 gutterCache[uri] = infos
4151 cacheTimestamps[uri] = System .currentTimeMillis()
4252 }
43-
53+
4454 fun getCacheAge (uri : String ): Long {
4555 val timestamp = cacheTimestamps[uri] ? : return Long .MAX_VALUE
4656 return System .currentTimeMillis() - timestamp
4757 }
48-
58+
4959 fun isCacheStale (uri : String , maxAgeMs : Long = 1000): Boolean {
5060 return getCacheAge(uri) > maxAgeMs
5161 }
@@ -57,28 +67,28 @@ object LuaGutterCacheManager {
5767class LuaDocumentListener (private val project : Project ) : DocumentListener {
5868 private val updateScheduler = mutableMapOf<Document , Long >()
5969 private val pendingUpdates = mutableMapOf<Document , Runnable >()
60-
70+
6171 override fun documentChanged (event : DocumentEvent ) {
6272 val document = event.document
6373 val file = FileDocumentManager .getInstance().getFile(document) ? : return
64-
65- if (file.extension != " lua " ) return
66-
74+
75+ if (file.fileType != = LuaFileType . INSTANCE ) return
76+
6777 // Clear cache immediately for instant refresh
6878 LuaGutterCacheManager .clearCache(file.url)
69-
79+
7080 // Cancel any pending update
71- pendingUpdates[document]?.let {
81+ pendingUpdates[document]?.let {
7282 // The runnable will be replaced
7383 }
74-
84+
7585 // Schedule restart of code analysis with shorter debounce (200ms)
7686 val now = System .currentTimeMillis()
7787 val lastUpdate = updateScheduler[document] ? : 0
78-
88+
7989 // Reduced debounce time to 200ms for better responsiveness
8090 val debounceTime = 200L
81-
91+
8292 val updateRunnable = Runnable {
8393 ApplicationManager .getApplication().invokeLater {
8494 val psiFile = PsiManager .getInstance(project).findFile(file)
@@ -87,9 +97,9 @@ class LuaDocumentListener(private val project: Project) : DocumentListener {
8797 }
8898 }
8999 }
90-
100+
91101 pendingUpdates[document] = updateRunnable
92-
102+
93103 if (now - lastUpdate > debounceTime) {
94104 updateScheduler[document] = now
95105 // Execute immediately if enough time has passed
@@ -115,10 +125,10 @@ class LuaDocumentListener(private val project: Project) : DocumentListener {
115125 */
116126class LuaFileEditorListener (private val project : Project ) : FileEditorManagerListener {
117127 override fun fileOpened (source : FileEditorManager , file : VirtualFile ) {
118- if (file.extension == " lua " ) {
128+ if (file.fileType == = LuaFileType . INSTANCE ) {
119129 // Clear cache for newly opened file to ensure fresh data
120130 LuaGutterCacheManager .clearCache(file.url)
121-
131+
122132 // Trigger code analysis
123133 ApplicationManager .getApplication().invokeLater {
124134 val psiFile = PsiManager .getInstance(project).findFile(file)
@@ -137,38 +147,32 @@ class LuaGutterCacheStartupActivity : ProjectActivity {
137147 override suspend fun execute (project : Project ) {
138148 // Register document listener
139149 val documentListener = LuaDocumentListener (project)
140- val connection = ApplicationManager .getApplication().messageBus.connect(project)
141-
150+ val parentDisposable = project.service<LuaGutterCacheListenerDisposable >()
151+ val appConnection = ApplicationManager .getApplication().messageBus.connect(parentDisposable)
152+ val projectConnection = project.messageBus.connect(parentDisposable)
153+
142154 // Listen to editor creation events to attach document listener
143- val editorFactory = com.intellij.openapi.editor.EditorFactory .getInstance()
144- editorFactory.addEditorFactoryListener(
145- object : com.intellij.openapi.editor.event.EditorFactoryListener {
146- override fun editorCreated (event : com.intellij.openapi.editor.event.EditorFactoryEvent ) {
147- event.editor.document.addDocumentListener(documentListener, project)
148- }
149- },
150- project
151- )
152-
155+ EditorFactory .getInstance()
156+ .eventMulticaster
157+ .addDocumentListener(documentListener, parentDisposable)
158+
153159 // Register file editor listener
154- project.messageBus
155- .connect()
156- .subscribe(
157- FileEditorManagerListener .FILE_EDITOR_MANAGER ,
158- LuaFileEditorListener (project)
159- )
160-
160+ projectConnection.subscribe(
161+ FileEditorManagerListener .FILE_EDITOR_MANAGER ,
162+ LuaFileEditorListener (project)
163+ )
164+
161165 // Register bulk file listener to detect external changes
162- connection .subscribe(
163- com.intellij.openapi.vfs. VirtualFileManager .VFS_CHANGES ,
164- object : com.intellij.openapi.vfs.newvfs. BulkFileListener {
165- override fun after (events : List <com.intellij.openapi.vfs.newvfs.events. VFileEvent >) {
166+ appConnection .subscribe(
167+ VirtualFileManager .VFS_CHANGES ,
168+ object : BulkFileListener {
169+ override fun after (events : List <VFileEvent >) {
166170 for (event in events) {
167171 val file = event.file
168- if (file != null && file.extension == " lua " ) {
172+ if (file != null && file.fileType == = LuaFileType . INSTANCE ) {
169173 // Clear cache when file changes externally
170174 LuaGutterCacheManager .clearCache(file.url)
171-
175+
172176 // Trigger update
173177 ApplicationManager .getApplication().invokeLater {
174178 val psiFile = PsiManager .getInstance(project).findFile(file)
@@ -183,3 +187,10 @@ class LuaGutterCacheStartupActivity : ProjectActivity {
183187 )
184188 }
185189}
190+
191+ @Service(PROJECT )
192+ class LuaGutterCacheListenerDisposable : Disposable {
193+ override fun dispose () {
194+ // IntelliJ will automatically dispose of it when the project is disposed
195+ }
196+ }
0 commit comments