Skip to content

Commit 24a61e7

Browse files
committed
refactor(折叠逻辑): 优化TabooLib翻译折叠线程与EDT安全执行
重构代码以在EDT中安全执行折叠操作,使用ReadAction.nonBlocking和AppExecutorUtil提升多线程安全性,避免编辑器访问违规。
1 parent eb878c3 commit 24a61e7

File tree

5 files changed

+126
-81
lines changed

5 files changed

+126
-81
lines changed

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

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -125,38 +125,42 @@ class LangFoldingDocumentListener : ProjectActivity {
125125
val foldingManager = CodeFoldingManager.getInstance(project)
126126
val foldingModel = editor.foldingModel
127127

128-
// 在写入操作中更新折叠
129-
ApplicationManager.getApplication().runWriteAction {
130-
try {
131-
foldingModel.runBatchFoldingOperation {
132-
// 重新构建折叠区域
133-
foldingManager.updateFoldRegions(editor)
134-
135-
// 获取当前光标位置
136-
val caretOffset = editor.caretModel.offset
137-
138-
// 智能处理TabooLib翻译折叠区域
139-
for (foldRegion in foldingModel.allFoldRegions) {
140-
val group = foldRegion.group
141-
if (group?.toString()?.startsWith("taboolib.translation.") == true) {
142-
if (LangFoldingSettings.instance.shouldFoldTranslations) {
143-
// 检查光标是否在折叠区域内
144-
val isCaretInside = caretOffset >= foldRegion.startOffset &&
145-
caretOffset <= foldRegion.endOffset
146-
147-
if (isCaretInside) {
148-
// 如果光标在折叠区域内,展开它
149-
foldRegion.isExpanded = true
150-
} else {
151-
// 如果光标不在折叠区域内,折叠它
152-
foldRegion.isExpanded = false
128+
// 确保在 EDT 中执行写操作
129+
ApplicationManager.getApplication().invokeLater {
130+
if (!editor.isDisposed && !project.isDisposed) {
131+
ApplicationManager.getApplication().runWriteAction {
132+
try {
133+
foldingModel.runBatchFoldingOperation {
134+
// 重新构建折叠区域
135+
foldingManager.updateFoldRegions(editor)
136+
137+
// 获取当前光标位置
138+
val caretOffset = editor.caretModel.offset
139+
140+
// 智能处理TabooLib翻译折叠区域
141+
for (foldRegion in foldingModel.allFoldRegions) {
142+
val group = foldRegion.group
143+
if (group?.toString()?.startsWith("taboolib.translation.") == true) {
144+
if (LangFoldingSettings.instance.shouldFoldTranslations) {
145+
// 检查光标是否在折叠区域内
146+
val isCaretInside = caretOffset >= foldRegion.startOffset &&
147+
caretOffset <= foldRegion.endOffset
148+
149+
if (isCaretInside) {
150+
// 如果光标在折叠区域内,展开它
151+
foldRegion.isExpanded = true
152+
} else {
153+
// 如果光标不在折叠区域内,折叠它
154+
foldRegion.isExpanded = false
155+
}
156+
}
153157
}
154158
}
155159
}
160+
} catch (e: Exception) {
161+
// 忽略异常,避免影响编辑器性能
156162
}
157163
}
158-
} catch (e: Exception) {
159-
// 忽略异常,避免影响编辑器性能
160164
}
161165
}
162166
}

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

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -86,20 +86,31 @@ class LangFoldingEditorListener : ProjectActivity {
8686
if (psiFile?.language?.id == "kotlin") {
8787
val foldingManager = CodeFoldingManager.getInstance(project)
8888

89-
ApplicationManager.getApplication().runWriteAction {
90-
try {
91-
foldingManager.updateFoldRegions(editor)
92-
93-
// 延迟应用初始折叠状态
94-
ApplicationManager.getApplication().invokeLater({
89+
// 使用 ReadAction.nonBlocking 避免 EDT 线程访问违规
90+
com.intellij.openapi.application.ReadAction.nonBlocking {
91+
// 在后台线程中准备数据
92+
if (!editor.isDisposed && !project.isDisposed) {
93+
// 切换到 EDT 执行写操作
94+
ApplicationManager.getApplication().invokeLater {
9595
if (!editor.isDisposed && !project.isDisposed) {
96-
applyInitialFoldingState(editor, project)
96+
ApplicationManager.getApplication().runWriteAction {
97+
try {
98+
foldingManager.updateFoldRegions(editor)
99+
100+
// 延迟应用初始折叠状态
101+
ApplicationManager.getApplication().invokeLater({
102+
if (!editor.isDisposed && !project.isDisposed) {
103+
applyInitialFoldingState(editor, project)
104+
}
105+
}, project.disposed)
106+
} catch (e: Exception) {
107+
// 忽略异常
108+
}
109+
}
97110
}
98-
}, project.disposed)
99-
} catch (e: Exception) {
100-
// 忽略异常
111+
}
101112
}
102-
}
113+
}.submit(com.intellij.util.concurrency.AppExecutorUtil.getAppExecutorService())
103114
}
104115
}
105116

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

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -70,39 +70,43 @@ class LangFoldingMouseListener : ProjectActivity {
7070
val psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document)
7171

7272
// 只处理Kotlin文件
73-
if (psiFile?.language?.id != "kotlin") return
74-
75-
val foldingModel = editor.foldingModel
76-
77-
// 在写入操作中更新折叠
78-
ApplicationManager.getApplication().runWriteAction {
79-
try {
80-
foldingModel.runBatchFoldingOperation {
81-
// 获取当前光标位置
82-
val caretOffset = editor.caretModel.offset
83-
84-
// 检查所有TabooLib翻译折叠区域
85-
for (foldRegion in foldingModel.allFoldRegions) {
86-
val group = foldRegion.group
87-
if (group?.toString()?.startsWith("taboolib.translation.") == true) {
88-
if (LangFoldingSettings.instance.shouldFoldTranslations) {
89-
// 检查光标是否在折叠区域内
90-
val isCaretInside = caretOffset >= foldRegion.startOffset &&
91-
caretOffset <= foldRegion.endOffset
73+
if (psiFile?.language?.id == "kotlin") {
74+
val foldingModel = editor.foldingModel
75+
76+
// 确保在 EDT 中执行写操作
77+
ApplicationManager.getApplication().invokeLater {
78+
if (!editor.isDisposed && !project.isDisposed) {
79+
ApplicationManager.getApplication().runWriteAction {
80+
try {
81+
foldingModel.runBatchFoldingOperation {
82+
// 获取当前光标位置
83+
val caretOffset = editor.caretModel.offset
9284

93-
if (isCaretInside) {
94-
// 如果光标在折叠区域内,展开它
95-
foldRegion.isExpanded = true
96-
} else {
97-
// 如果光标不在折叠区域内,折叠它
98-
foldRegion.isExpanded = false
85+
// 智能处理TabooLib翻译折叠区域
86+
for (foldRegion in foldingModel.allFoldRegions) {
87+
val group = foldRegion.group
88+
if (group?.toString()?.startsWith("taboolib.translation.") == true) {
89+
if (LangFoldingSettings.instance.shouldFoldTranslations) {
90+
// 检查光标是否在折叠区域内
91+
val isCaretInside = caretOffset >= foldRegion.startOffset &&
92+
caretOffset <= foldRegion.endOffset
93+
94+
if (isCaretInside) {
95+
// 如果光标在折叠区域内,展开它
96+
foldRegion.isExpanded = true
97+
} else {
98+
// 如果光标不在折叠区域内,折叠它
99+
foldRegion.isExpanded = false
100+
}
101+
}
102+
}
99103
}
100104
}
105+
} catch (e: Exception) {
106+
// 忽略异常,避免影响编辑器性能
101107
}
102108
}
103109
}
104-
} catch (e: Exception) {
105-
// 忽略异常,避免影响编辑器性能
106110
}
107111
}
108112
}

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

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ package org.tabooproject.development.inlay
22

33
import com.intellij.codeInsight.folding.CodeFoldingManager
44
import com.intellij.openapi.application.ApplicationManager
5+
import com.intellij.openapi.application.ReadAction
56
import com.intellij.openapi.editor.Editor
67
import com.intellij.openapi.editor.EditorFactory
78
import com.intellij.openapi.fileEditor.FileEditorManager
89
import com.intellij.openapi.project.Project
910
import com.intellij.openapi.startup.ProjectActivity
1011
import com.intellij.psi.PsiDocumentManager
12+
import com.intellij.util.concurrency.AppExecutorUtil
1113

1214
/**
1315
* TabooLib语言文件折叠刷新监听器
@@ -19,36 +21,51 @@ import com.intellij.psi.PsiDocumentManager
1921
class LangFoldingRefreshListener : ProjectActivity {
2022

2123
override suspend fun execute(project: Project) {
22-
// 项目打开后,延迟刷新代码折叠
23-
ApplicationManager.getApplication().invokeLater {
24+
// 项目打开后,在后台线程延迟刷新代码折叠
25+
ReadAction.nonBlocking {
2426
refreshFoldingInAllEditors(project)
25-
}
27+
}.submit(AppExecutorUtil.getAppExecutorService())
2628
}
2729

2830
private fun refreshFoldingInAllEditors(project: Project) {
31+
if (project.isDisposed) return
32+
2933
val fileEditorManager = FileEditorManager.getInstance(project)
3034
val psiDocumentManager = PsiDocumentManager.getInstance(project)
3135

3236
// 遍历所有打开的编辑器
3337
for (editor in EditorFactory.getInstance().allEditors) {
34-
if (editor.project == project) {
38+
if (editor.project == project && !editor.isDisposed) {
3539
refreshFoldingInEditor(editor, project, psiDocumentManager)
3640
}
3741
}
3842
}
3943

4044
private fun refreshFoldingInEditor(editor: Editor, project: Project, psiDocumentManager: PsiDocumentManager) {
45+
if (editor.isDisposed || project.isDisposed) return
46+
4147
val document = editor.document
4248
val psiFile = psiDocumentManager.getPsiFile(document)
4349

4450
// 只处理Kotlin文件
4551
if (psiFile?.language?.id == "kotlin") {
4652
val foldingManager = CodeFoldingManager.getInstance(project)
4753

48-
// 刷新代码折叠
49-
ApplicationManager.getApplication().runWriteAction {
50-
foldingManager.buildInitialFoldings(editor)
51-
}
54+
// 在后台线程中执行,避免 EDT 线程访问违规
55+
ReadAction.nonBlocking {
56+
// 在 EDT 中执行写操作
57+
ApplicationManager.getApplication().invokeLater {
58+
if (!editor.isDisposed && !project.isDisposed) {
59+
ApplicationManager.getApplication().runWriteAction {
60+
try {
61+
foldingManager.buildInitialFoldings(editor)
62+
} catch (e: Exception) {
63+
// 忽略异常,避免影响编辑器性能
64+
}
65+
}
66+
}
67+
}
68+
}.submit(AppExecutorUtil.getAppExecutorService())
5269
}
5370
}
5471
}

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

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ package org.tabooproject.development.inlay
22

33
import com.intellij.codeInsight.folding.CodeFoldingManager
44
import com.intellij.openapi.application.ApplicationManager
5+
import com.intellij.openapi.application.ReadAction
56
import com.intellij.openapi.editor.EditorFactory
67
import com.intellij.openapi.project.Project
78
import com.intellij.openapi.project.ProjectManager
89
import com.intellij.openapi.startup.ProjectActivity
10+
import com.intellij.util.concurrency.AppExecutorUtil
911

1012
/**
1113
* TabooLib语言文件折叠设置变更监听器
@@ -27,29 +29,36 @@ class LangFoldingSettingsListener : ProjectActivity {
2729
* 刷新所有打开编辑器的折叠状态
2830
*/
2931
fun refreshAllEditors() {
30-
ApplicationManager.getApplication().invokeLater {
32+
ReadAction.nonBlocking {
3133
val projects = ProjectManager.getInstance().openProjects
3234
for (project in projects) {
3335
if (!project.isDisposed) {
3436
refreshProjectEditors(project)
3537
}
3638
}
37-
}
39+
}.submit(AppExecutorUtil.getAppExecutorService())
3840
}
3941

4042
/**
4143
* 刷新指定项目的所有编辑器折叠状态
4244
*/
4345
private fun refreshProjectEditors(project: Project) {
46+
if (project.isDisposed) return
47+
4448
val editors = EditorFactory.getInstance().allEditors
4549
for (editor in editors) {
4650
if (editor.project == project && !editor.isDisposed) {
47-
ApplicationManager.getApplication().runWriteAction {
48-
try {
49-
val foldingManager = CodeFoldingManager.getInstance(project)
50-
foldingManager.buildInitialFoldings(editor)
51-
} catch (e: Exception) {
52-
// 忽略异常
51+
// 在 EDT 中执行写操作
52+
ApplicationManager.getApplication().invokeLater {
53+
if (!editor.isDisposed && !project.isDisposed) {
54+
ApplicationManager.getApplication().runWriteAction {
55+
try {
56+
val foldingManager = CodeFoldingManager.getInstance(project)
57+
foldingManager.buildInitialFoldings(editor)
58+
} catch (e: Exception) {
59+
// 忽略异常
60+
}
61+
}
5362
}
5463
}
5564
}

0 commit comments

Comments
 (0)