Skip to content

Commit 0ca1dee

Browse files
committed
refactor: 优化编辑器监听与缓存机制
优化了编辑器文档、语言文件、折叠等监听器的性能,引入防抖机制减少频繁刷新,改进缓存策略避免内存泄漏,并增强异常处理与日志记录。
1 parent dafb38c commit 0ca1dee

File tree

6 files changed

+343
-163
lines changed

6 files changed

+343
-163
lines changed

src/main/kotlin/org/tabooproject/development/inlay/EditorDocumentListener.kt

Lines changed: 59 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import com.intellij.openapi.fileEditor.FileEditorManager
1111
import com.intellij.openapi.project.Project
1212
import com.intellij.openapi.vfs.VirtualFile
1313
import com.intellij.psi.PsiManager
14+
import com.intellij.openapi.application.ApplicationManager
1415

1516
/**
1617
* 编辑器文档监听器
@@ -22,6 +23,13 @@ import com.intellij.psi.PsiManager
2223
@Service(Service.Level.PROJECT)
2324
class EditorDocumentListener(private val project: Project) : Disposable {
2425

26+
/**
27+
* 防抖时间戳
28+
*/
29+
@Volatile
30+
private var lastRefreshTime = 0L
31+
private val refreshDebounceMs = 200L
32+
2533
/**
2634
* 文档监听器
2735
*/
@@ -31,8 +39,19 @@ class EditorDocumentListener(private val project: Project) : Disposable {
3139
val file = FileDocumentManager.getInstance().getFile(document)
3240

3341
if (file != null && isLanguageFile(file)) {
34-
// 立即刷新所有编辑器
35-
refreshAllEditors()
42+
// 防抖:避免频繁刷新
43+
val currentTime = System.currentTimeMillis()
44+
if (currentTime - lastRefreshTime < refreshDebounceMs) {
45+
return
46+
}
47+
lastRefreshTime = currentTime
48+
49+
// 延迟刷新以合并连续的修改
50+
ApplicationManager.getApplication().invokeLater({
51+
if (!project.isDisposed) {
52+
refreshEditorsForFile(file)
53+
}
54+
}, project.disposed)
3655
}
3756
}
3857
}
@@ -92,7 +111,44 @@ class EditorDocumentListener(private val project: Project) : Disposable {
92111
}
93112

94113
/**
95-
* 刷新所有编辑器
114+
* 针对特定文件刷新相关编辑器(优化版本)
115+
*/
116+
private fun refreshEditorsForFile(changedFile: VirtualFile) {
117+
// 清除该文件的缓存
118+
LangParser.clearCache(changedFile)
119+
120+
// 只刷新包含 sendLang 调用的 Kotlin 文件
121+
val fileEditorManager = FileEditorManager.getInstance(project)
122+
val psiManager = PsiManager.getInstance(project)
123+
val daemonCodeAnalyzer = com.intellij.codeInsight.daemon.DaemonCodeAnalyzer.getInstance(project)
124+
125+
fileEditorManager.allEditors.forEach { editor ->
126+
if (editor is com.intellij.openapi.fileEditor.TextEditor) {
127+
val virtualFile = editor.file
128+
if (virtualFile != null && virtualFile.name.endsWith(".kt")) {
129+
val psiFile = psiManager.findFile(virtualFile)
130+
if (psiFile != null && containsSendLangCalls(psiFile)) {
131+
// 只重新分析包含 sendLang 调用的文件
132+
daemonCodeAnalyzer.restart(psiFile)
133+
}
134+
}
135+
}
136+
}
137+
}
138+
139+
/**
140+
* 检查文件是否包含 sendLang 调用(简单启发式检查)
141+
*/
142+
private fun containsSendLangCalls(psiFile: com.intellij.psi.PsiFile): Boolean {
143+
return try {
144+
psiFile.text.contains("sendLang")
145+
} catch (e: Exception) {
146+
true // 发生异常时默认刷新
147+
}
148+
}
149+
150+
/**
151+
* 刷新所有编辑器(保留作为备用方法)
96152
*/
97153
private fun refreshAllEditors() {
98154
// 清除所有缓存
@@ -118,23 +174,6 @@ class EditorDocumentListener(private val project: Project) : Disposable {
118174
}
119175
}
120176
}
121-
122-
// 延迟再次刷新,确保变更生效
123-
com.intellij.openapi.application.ApplicationManager.getApplication().invokeLater {
124-
if (!project.isDisposed) {
125-
fileEditorManager.allEditors.forEach { editor ->
126-
if (editor is com.intellij.openapi.fileEditor.TextEditor) {
127-
val virtualFile = editor.file
128-
if (virtualFile != null) {
129-
val psiFile = psiManager.findFile(virtualFile)
130-
if (psiFile != null) {
131-
daemonCodeAnalyzer.restart(psiFile)
132-
}
133-
}
134-
}
135-
}
136-
}
137-
}
138177
}
139178
}
140179
}

src/main/kotlin/org/tabooproject/development/inlay/LangFileListener.kt

Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import com.intellij.openapi.vfs.VirtualFile
66
import com.intellij.openapi.vfs.newvfs.BulkFileListener
77
import com.intellij.openapi.vfs.newvfs.events.VFileEvent
88
import com.intellij.ui.EditorNotifications
9+
import java.util.concurrent.atomic.AtomicLong
910

1011
/**
1112
* TabooLib语言文件监听器
@@ -15,45 +16,64 @@ import com.intellij.ui.EditorNotifications
1516
* @since 1.42
1617
*/
1718
object LangFileListener : BulkFileListener {
19+
20+
private val lastRefreshTime = AtomicLong(0)
21+
private const val REFRESH_DEBOUNCE_MS = 300L // 防抖间隔
1822

1923
override fun before(events: List<VFileEvent>) {
2024
// 文件修改前无需处理
2125
}
2226

2327
override fun after(events: List<VFileEvent>) {
2428
// 检查是否有语言文件发生变更
25-
if (events.any { LangFiles.isLangFile(it.file) }) {
26-
// 清除受影响文件的缓存
27-
events.forEach { event ->
28-
event.file?.let { file ->
29-
LangParser.clearCache(file)
30-
}
31-
}
32-
33-
// 更新所有编辑器通知
34-
EditorNotifications.updateAll()
35-
36-
// 更新所有项目的代码折叠和行标记
37-
updateAllProjects()
29+
val affectedFiles = events.mapNotNull { it.file }.filter { LangFiles.isLangFile(it) }
30+
if (affectedFiles.isEmpty()) return
31+
32+
// 防抖:避免频繁刷新
33+
val currentTime = System.currentTimeMillis()
34+
if (currentTime - lastRefreshTime.get() < REFRESH_DEBOUNCE_MS) {
35+
return
36+
}
37+
lastRefreshTime.set(currentTime)
38+
39+
// 清除受影响文件的缓存
40+
affectedFiles.forEach { file ->
41+
LangParser.clearCache(file)
42+
}
43+
44+
// 更新编辑器通知(轻量级操作)
45+
EditorNotifications.updateAll()
46+
47+
// 延迟刷新项目,避免阻塞文件操作
48+
ApplicationManager.getApplication().invokeLater {
49+
updateAffectedProjects(affectedFiles)
3850
}
3951
}
4052

4153
/**
42-
* 更新所有项目
54+
* 更新受影响的项目(优化版本)
4355
*/
44-
private fun updateAllProjects() {
45-
ApplicationManager.getApplication().invokeLater {
46-
val openProjects = com.intellij.openapi.project.ProjectManager.getInstance().openProjects
47-
for (project in openProjects) {
48-
if (!project.isDisposed) {
49-
refreshProject(project)
50-
}
56+
private fun updateAffectedProjects(affectedFiles: List<VirtualFile>) {
57+
val projectManager = com.intellij.openapi.project.ProjectManager.getInstance()
58+
val openProjects = projectManager.openProjects
59+
60+
// 只刷新实际包含受影响文件的项目
61+
for (project in openProjects) {
62+
if (project.isDisposed) continue
63+
64+
val projectBasePath = project.basePath ?: continue
65+
val hasAffectedFiles = affectedFiles.any { file ->
66+
file.path.startsWith(projectBasePath)
67+
}
68+
69+
if (hasAffectedFiles) {
70+
refreshProject(project)
5171
}
5272
}
5373
}
5474

5575
/**
56-
* 刷新项目
76+
* 刷新项目(优化版本)
5777
*/
5878
private fun refreshProject(project: Project) {
5979
if (project.isDisposed) return
@@ -63,27 +83,19 @@ object LangFileListener : BulkFileListener {
6383
val fileEditorManager = com.intellij.openapi.fileEditor.FileEditorManager.getInstance(project)
6484
val daemonCodeAnalyzer = com.intellij.codeInsight.daemon.DaemonCodeAnalyzer.getInstance(project)
6585

66-
// 刷新当前打开的所有编辑器
86+
// 只刷新当前打开的 Kotlin 文件
6787
fileEditorManager.allEditors.forEach { editor ->
6888
if (editor is com.intellij.openapi.fileEditor.TextEditor) {
6989
val virtualFile = editor.file
70-
if (virtualFile != null && virtualFile.isValid) {
90+
if (virtualFile != null && virtualFile.isValid && virtualFile.name.endsWith(".kt")) {
7191
val psiFile = psiManager.findFile(virtualFile)
7292
if (psiFile != null) {
73-
// 强制重新分析整个文件
93+
// 只重新分析包含 sendLang 调用的文件
7494
daemonCodeAnalyzer.restart(psiFile)
7595
}
7696
}
7797
}
7898
}
79-
80-
// 强制更新折叠区域和行标记
81-
val updateManager = com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl.getInstance(project)
82-
updateManager.setUpdateByTimerEnabled(false) // 暂时关闭自动更新
83-
updateManager.setUpdateByTimerEnabled(true) // 重新开启自动更新
84-
85-
// 刷新所有编辑器
86-
com.intellij.codeInsight.daemon.DaemonCodeAnalyzer.getInstance(project).restart()
8799
}
88100
}
89101
}

0 commit comments

Comments
 (0)