Skip to content

Commit 25db016

Browse files
优化:整理与完善本地化的复制、替换、生成、翻译等操作
1 parent 68d9842 commit 25db016

31 files changed

+325
-77
lines changed

src/main/kotlin/icu/windea/pls/PlsConstants.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,6 @@ object PlsConstants {
2424
//val eraseMarker = TextAttributes()
2525
//val onlyForegroundAttributesFlags = WithAttributesPresentation.AttributesFlags().withSkipBackground(true).withSkipEffects(true)
2626

27-
object Ids {
28-
const val translationPlugin = "cn.yiiguxing.plugin.translate"
29-
}
30-
3127
object Strings {
3228
const val anonymous = "(anonymous)"
3329
const val unknown = "(unknown)"
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package icu.windea.pls.lang.intentions.localisation
2+
3+
import com.intellij.notification.*
4+
import com.intellij.openapi.editor.*
5+
import com.intellij.openapi.ide.*
6+
import com.intellij.openapi.project.*
7+
import com.intellij.openapi.ui.popup.*
8+
import com.intellij.platform.ide.progress.*
9+
import com.intellij.psi.*
10+
import icu.windea.pls.*
11+
import icu.windea.pls.config.config.*
12+
import icu.windea.pls.lang.*
13+
import icu.windea.pls.lang.search.*
14+
import icu.windea.pls.lang.search.selector.*
15+
import icu.windea.pls.lang.ui.locale.*
16+
import icu.windea.pls.lang.util.*
17+
import icu.windea.pls.localisation.psi.*
18+
import icu.windea.pls.model.*
19+
import kotlinx.coroutines.*
20+
import java.awt.datatransfer.*
21+
22+
/**
23+
* 复制来自特定语言区域的本地化(光标位置对应的本地化,或者光标选取范围涉及到的所有本地化)到剪贴板。
24+
*
25+
* 复制的文本格式为:`KEY:0 "TEXT"`
26+
*/
27+
class CopyLocalisationFromLocaleIntention : CopyLocalisationIntentionBase() {
28+
override fun getFamilyName() = PlsBundle.message("intention.copyLocalisationFromLocale")
29+
30+
override fun doInvoke(project: Project, editor: Editor?, file: PsiFile?, elements: List<ParadoxLocalisationProperty>) {
31+
if (editor == null) return
32+
val allLocales = ParadoxLocaleManager.getLocaleConfigs()
33+
val localePopup = ParadoxLocaleListPopup(allLocales)
34+
localePopup.doFinalStep {
35+
val selectedLocale = localePopup.selectedLocale ?: return@doFinalStep
36+
val coroutineScope = PlsFacade.getCoroutineScope(project)
37+
coroutineScope.launch {
38+
val text = getText(project, file, elements, selectedLocale) ?: return@launch
39+
copyText(project, text, selectedLocale)
40+
}
41+
}
42+
JBPopupFactory.getInstance().createListPopup(localePopup).showInBestPositionFor(editor)
43+
}
44+
45+
private suspend fun getText(project: Project, file: PsiFile?, elements: List<ParadoxLocalisationProperty>, locale: CwtLocalisationLocaleConfig): String? {
46+
return withBackgroundProgress(project, "Copy localisation(s) to the clipboard from specified locale (target locale: ${locale})") {
47+
elements.map { it to ParadoxLocalisationSnippets.from(it) }
48+
.filter { (_, snippets) -> snippets.text.isNotBlank() }
49+
.map { (_, snippets) ->
50+
async {
51+
val newText = getNewText(project, file, snippets, locale) ?: return@async
52+
snippets.newText = newText
53+
}
54+
}.awaitAll()
55+
elements.map { it to ParadoxLocalisationSnippets.from(it) }.joinToString("\n") { it.second.renderNew() }
56+
}
57+
}
58+
59+
private fun getNewText(project: Project, file: PsiFile?, snippets: ParadoxLocalisationSnippets, locale: CwtLocalisationLocaleConfig): String? {
60+
val selector = selector(project, file).localisation().contextSensitive().locale(locale)
61+
val e = ParadoxLocalisationSearch.search(snippets.key, selector).find() ?: return null
62+
val newText = e.value ?: return null
63+
return newText
64+
}
65+
66+
private fun copyText(project: Project, text: String, locale: CwtLocalisationLocaleConfig) {
67+
createNotification(PlsBundle.message("intention.copyLocalisationFromLocale.notification.success", locale), NotificationType.INFORMATION).notify(project)
68+
CopyPasteManager.getInstance().setContents(StringSelection(text))
69+
}
70+
}
Lines changed: 15 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,41 @@
11
package icu.windea.pls.lang.intentions.localisation
22

3-
import com.intellij.codeInsight.intention.*
4-
import com.intellij.codeInsight.intention.preview.*
53
import com.intellij.notification.*
6-
import com.intellij.openapi.editor.*
4+
import com.intellij.openapi.editor.Editor
75
import com.intellij.openapi.ide.*
86
import com.intellij.openapi.project.*
97
import com.intellij.psi.*
10-
import com.intellij.psi.util.*
118
import icu.windea.pls.*
12-
import icu.windea.pls.core.collections.*
9+
import icu.windea.pls.config.config.CwtLocalisationLocaleConfig
1310
import icu.windea.pls.lang.*
14-
import icu.windea.pls.localisation.*
11+
import icu.windea.pls.lang.util.ParadoxLocalisationManager
1512
import icu.windea.pls.localisation.psi.*
13+
import kotlinx.coroutines.launch
1614
import java.awt.datatransfer.*
1715

1816
/**
1917
* 复制本地化(光标位置对应的本地化,或者光标选取范围涉及到的所有本地化)到剪贴板。
2018
*
2119
* 复制的文本格式为:`KEY:0 "TEXT"`
2220
*/
23-
open class CopyLocalisationIntention : IntentionAction, DumbAware {
21+
class CopyLocalisationIntention : CopyLocalisationIntentionBase() {
2422
override fun getFamilyName() = PlsBundle.message("intention.copyLocalisation")
2523

26-
override fun getText() = familyName
27-
28-
override fun isAvailable(project: Project, editor: Editor?, file: PsiFile?): Boolean {
29-
if (editor == null || file == null) return false
30-
val selectionStart = editor.selectionModel.selectionStart
31-
val selectionEnd = editor.selectionModel.selectionEnd
32-
if (file.language !is ParadoxLocalisationLanguage) return false
33-
val hasElements = if (selectionStart == selectionEnd) {
34-
val originalElement = file.findElementAt(selectionStart)
35-
originalElement?.parentOfType<ParadoxLocalisationProperty>() != null
36-
} else {
37-
val originalStartElement = file.findElementAt(selectionStart) ?: return false
38-
val originalEndElement = file.findElementAt(selectionEnd)
39-
hasLocalisationPropertiesBetween(originalStartElement, originalEndElement)
24+
override fun doInvoke(project: Project, editor: Editor?, file: PsiFile?, elements: List<ParadoxLocalisationProperty>) {
25+
val coroutineScope = PlsFacade.getCoroutineScope(project)
26+
coroutineScope.launch {
27+
val text = getText(project, file, elements) ?: return@launch
28+
copyText(project, text)
4029
}
41-
return hasElements
4230
}
4331

44-
override fun invoke(project: Project, editor: Editor?, file: PsiFile?) {
45-
if (editor == null || file == null) return
46-
val selectionStart = editor.selectionModel.selectionStart
47-
val selectionEnd = editor.selectionModel.selectionEnd
48-
val elements = if (selectionStart == selectionEnd) {
49-
val originalElement = file.findElementAt(selectionStart)
50-
originalElement?.parentOfType<ParadoxLocalisationProperty>().toSingletonListOrEmpty()
51-
} else {
52-
val originalStartElement = file.findElementAt(selectionStart) ?: return
53-
val originalEndElement = file.findElementAt(selectionEnd)
54-
findLocalisationPropertiesBetween(originalStartElement, originalEndElement)
55-
}
56-
if (elements.isEmpty()) return
57-
58-
doInvoke(project, editor, file, elements)
32+
private fun getText(project: Project, file: PsiFile?, elements: List<ParadoxLocalisationProperty>): String? {
33+
val text = elements.joinToString("\n") { it.text }
34+
return text
5935
}
6036

61-
protected open fun doInvoke(project: Project, editor: Editor?, file: PsiFile?, elements: List<ParadoxLocalisationProperty>) {
62-
val textToCopy = elements.joinToString("\n") { it.text }
37+
private fun copyText(project: Project, text: String) {
6338
createNotification(PlsBundle.message("intention.copyLocalisation.notification.success"), NotificationType.INFORMATION).notify(project)
64-
CopyPasteManager.getInstance().setContents(StringSelection(textToCopy))
39+
CopyPasteManager.getInstance().setContents(StringSelection(text))
6540
}
66-
67-
override fun generatePreview(project: Project, editor: Editor, file: PsiFile) = IntentionPreviewInfo.EMPTY
68-
69-
override fun startInWriteAction() = false
7041
}
71-
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package icu.windea.pls.lang.intentions.localisation
2+
3+
abstract class CopyLocalisationIntentionBase : ManipulateLocalisationIntentionBase()

src/main/kotlin/icu/windea/pls/lang/intentions/localisation/CopyTranslatedLocalisationIntention.kt renamed to src/main/kotlin/icu/windea/pls/lang/intentions/localisation/CopyLocalisationWithTranslationIntention.kt

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package icu.windea.pls.lang.intentions.localisation
22

33
import cn.yiiguxing.plugin.translate.trans.*
44
import cn.yiiguxing.plugin.translate.trans.Lang.Companion.isExplicit
5+
import com.intellij.codeInspection.util.IntentionFamilyName
56
import com.intellij.notification.*
67
import com.intellij.openapi.application.*
78
import com.intellij.openapi.diagnostic.*
@@ -14,7 +15,6 @@ import com.intellij.openapi.ui.popup.*
1415
import com.intellij.psi.*
1516
import icu.windea.pls.*
1617
import icu.windea.pls.config.config.*
17-
import icu.windea.pls.core.annotations.*
1818
import icu.windea.pls.extension.translation.*
1919
import icu.windea.pls.lang.*
2020
import icu.windea.pls.lang.ui.locale.*
@@ -30,9 +30,8 @@ import java.util.concurrent.atomic.*
3030
*
3131
* 复制的文本格式为:`KEY:0 "TEXT"`
3232
*/
33-
@WithExtension(PlsConstants.Ids.translationPlugin)
34-
class CopyTranslatedLocalisationIntention : CopyLocalisationIntention() {
35-
override fun getFamilyName() = PlsBundle.message("intention.copyTranslatedLocalisation")
33+
class CopyLocalisationWithTranslationIntention : CopyLocalisationIntentionBase() {
34+
override fun getFamilyName() = PlsBundle.message("intention.copyLocalisationWithTranslation")
3635

3736
override fun doInvoke(project: Project, editor: Editor?, file: PsiFile?, elements: List<ParadoxLocalisationProperty>) {
3837
if (editor == null || file == null) return
@@ -63,7 +62,7 @@ class CopyTranslatedLocalisationIntention : CopyLocalisationIntention() {
6362

6463
if (snippetsToTranslate.isEmpty()) {
6564
val textToCopy = snippetsList.joinToString("\n") { snippets -> snippets.joinToString("") { snippet -> snippet.text } }
66-
createNotification(PlsBundle.message("intention.copyTranslatedLocalisation.notification.1", targetLocale), NotificationType.INFORMATION).notify(project)
65+
createNotification(PlsBundle.message("intention.copyLocalisationWithTranslation.notification.1", targetLocale), NotificationType.INFORMATION).notify(project)
6766
CopyPasteManager.getInstance().setContents(StringSelection(textToCopy))
6867
return
6968
}
@@ -96,11 +95,11 @@ class CopyTranslatedLocalisationIntention : CopyLocalisationIntention() {
9695
val error = errorRef.get()
9796
if (error == null) {
9897
val textToCopy = snippetsList.joinToString("\n") { snippets -> snippets.joinToString("") { snippet -> snippet.text } }
99-
createNotification(PlsBundle.message("intention.copyTranslatedLocalisation.notification.0", targetLocale), NotificationType.INFORMATION).notify(project)
98+
createNotification(PlsBundle.message("intention.copyLocalisationWithTranslation.notification.0", targetLocale), NotificationType.INFORMATION).notify(project)
10099
CopyPasteManager.getInstance().setContents(StringSelection(textToCopy))
101100
} else {
102101
thisLogger().warn(error)
103-
createNotification(PlsBundle.message("intention.copyTranslatedLocalisation.notification.2", targetLocale), NotificationType.WARNING).notify(project)
102+
createNotification(PlsBundle.message("intention.copyLocalisationWithTranslation.notification.2", targetLocale), NotificationType.WARNING).notify(project)
104103
return@action
105104
}
106105
}
@@ -111,7 +110,7 @@ class CopyTranslatedLocalisationIntention : CopyLocalisationIntention() {
111110
val editorRef: WeakReference<Editor>,
112111
val total: Int,
113112
localeConfig: CwtLocalisationLocaleConfig
114-
) : BackgroundableProcessIndicator(project, PlsBundle.message("intention.copyTranslatedLocalisation.indicator.title", localeConfig), null, null, true) {
113+
) : BackgroundableProcessIndicator(project, PlsBundle.message("intention.copyLocalisationWithTranslation.indicator.title", localeConfig), null, null, true) {
115114
var current = 0
116115

117116
init {
@@ -136,7 +135,7 @@ class CopyTranslatedLocalisationIntention : CopyLocalisationIntention() {
136135
}
137136

138137
private fun setProgressText() {
139-
text = PlsBundle.message("intention.copyTranslatedLocalisation.indicator.text", current, total)
138+
text = PlsBundle.message("intention.copyLocalisationWithTranslation.indicator.text", current, total)
140139
}
141140

142141
fun checkProcessCanceledAndEditorDisposed(): Boolean {
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package icu.windea.pls.lang.intentions.localisation
2+
3+
import com.intellij.codeInsight.intention.IntentionAction
4+
import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo
5+
import com.intellij.openapi.editor.Editor
6+
import com.intellij.openapi.project.DumbAware
7+
import com.intellij.openapi.project.Project
8+
import com.intellij.psi.PsiFile
9+
import com.intellij.psi.util.parentOfType
10+
import icu.windea.pls.core.collections.toSingletonListOrEmpty
11+
import icu.windea.pls.localisation.ParadoxLocalisationLanguage
12+
import icu.windea.pls.localisation.psi.ParadoxLocalisationProperty
13+
import icu.windea.pls.localisation.psi.findLocalisationPropertiesBetween
14+
import icu.windea.pls.localisation.psi.hasLocalisationPropertiesBetween
15+
16+
abstract class ManipulateLocalisationIntentionBase : IntentionAction, DumbAware {
17+
override fun getText() = familyName
18+
19+
override fun isAvailable(project: Project, editor: Editor?, file: PsiFile?): Boolean {
20+
if (editor == null || file == null) return false
21+
val selectionStart = editor.selectionModel.selectionStart
22+
val selectionEnd = editor.selectionModel.selectionEnd
23+
if (file.language !is ParadoxLocalisationLanguage) return false
24+
val hasElements = if (selectionStart == selectionEnd) {
25+
val originalElement = file.findElementAt(selectionStart)
26+
originalElement?.parentOfType<ParadoxLocalisationProperty>() != null
27+
} else {
28+
val originalStartElement = file.findElementAt(selectionStart) ?: return false
29+
val originalEndElement = file.findElementAt(selectionEnd)
30+
hasLocalisationPropertiesBetween(originalStartElement, originalEndElement)
31+
}
32+
return hasElements
33+
}
34+
35+
override fun invoke(project: Project, editor: Editor?, file: PsiFile?) {
36+
if (editor == null || file == null) return
37+
val selectionStart = editor.selectionModel.selectionStart
38+
val selectionEnd = editor.selectionModel.selectionEnd
39+
val elements = if (selectionStart == selectionEnd) {
40+
val originalElement = file.findElementAt(selectionStart)
41+
originalElement?.parentOfType<ParadoxLocalisationProperty>().toSingletonListOrEmpty()
42+
} else {
43+
val originalStartElement = file.findElementAt(selectionStart) ?: return
44+
val originalEndElement = file.findElementAt(selectionEnd)
45+
findLocalisationPropertiesBetween(originalStartElement, originalEndElement)
46+
}
47+
if (elements.isEmpty()) return
48+
49+
doInvoke(project, editor, file, elements)
50+
}
51+
52+
protected abstract fun doInvoke(project: Project, editor: Editor?, file: PsiFile?, elements: List<ParadoxLocalisationProperty>)
53+
54+
//默认不显示预览,因为可能涉及异步调用
55+
override fun generatePreview(project: Project, editor: Editor, file: PsiFile) = IntentionPreviewInfo.EMPTY
56+
57+
override fun startInWriteAction() = false
58+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package icu.windea.pls.lang.intentions.localisation
2+
3+
import com.intellij.openapi.editor.Editor
4+
import com.intellij.openapi.project.Project
5+
import com.intellij.psi.PsiFile
6+
import icu.windea.pls.PlsBundle
7+
import icu.windea.pls.localisation.psi.ParadoxLocalisationProperty
8+
9+
class ReplaceLocalisationFromLocaleIntention : ReplaceLocalisationIntentionBase() {
10+
override fun getFamilyName() = PlsBundle.message("intention.replaceLocalisationFromLocale")
11+
12+
override fun doInvoke(project: Project, editor: Editor?, file: PsiFile?, elements: List<ParadoxLocalisationProperty>) {
13+
TODO("Not yet implemented")
14+
}
15+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package icu.windea.pls.lang.intentions.localisation
2+
3+
abstract class ReplaceLocalisationIntentionBase : ManipulateLocalisationIntentionBase()
4+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package icu.windea.pls.lang.intentions.localisation
2+
3+
import com.intellij.openapi.editor.Editor
4+
import com.intellij.openapi.project.Project
5+
import com.intellij.psi.PsiFile
6+
import icu.windea.pls.PlsBundle
7+
import icu.windea.pls.localisation.psi.ParadoxLocalisationProperty
8+
9+
class ReplaceLocalisationWithTranslationIntention : ReplaceLocalisationIntentionBase() {
10+
override fun getFamilyName() = PlsBundle.message("intention.replaceLocalisationWithTranslation")
11+
12+
override fun doInvoke(project: Project, editor: Editor?, file: PsiFile?, elements: List<ParadoxLocalisationProperty>) {
13+
TODO("Not yet implemented")
14+
}
15+
}

src/main/kotlin/icu/windea/pls/lang/util/ParadoxLocalisationManager.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ package icu.windea.pls.lang.util
33
import com.intellij.lang.*
44
import com.intellij.openapi.application.*
55
import com.intellij.openapi.progress.*
6+
import com.intellij.platform.util.coroutines.*
67
import com.intellij.psi.stubs.*
78
import com.intellij.psi.util.*
89
import icu.windea.pls.*
910
import icu.windea.pls.config.configGroup.*
1011
import icu.windea.pls.config.util.*
1112
import icu.windea.pls.core.*
13+
import icu.windea.pls.core.collections.*
1214
import icu.windea.pls.lang.*
1315
import icu.windea.pls.lang.search.*
1416
import icu.windea.pls.lang.search.selector.*
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package icu.windea.pls.model
2+
3+
import com.intellij.openapi.util.*
4+
import icu.windea.pls.localisation.psi.*
5+
6+
class ParadoxLocalisationSnippets(
7+
val key: String, // KEY
8+
val prefix: String, // KEY:0
9+
val text: String, // TEXT
10+
val textRange: TextRange,
11+
) {
12+
@Volatile
13+
var newText: String = text
14+
15+
fun render(): String {
16+
return "$prefix \"$text\"" // KEY:0 "TEXT"
17+
}
18+
19+
fun renderNew(): String {
20+
return "$prefix \"$newText\"" // KEY:0 "NEW TEXT"
21+
}
22+
23+
companion object {
24+
@JvmStatic
25+
fun from(element: ParadoxLocalisationProperty): ParadoxLocalisationSnippets {
26+
val name = element.name
27+
val elementText = element.text
28+
val i1 = elementText.indexOf('"')
29+
if (i1 == -1) {
30+
val prefix = elementText.trimEnd()
31+
val textRange = TextRange.from(elementText.length, 0)
32+
return ParadoxLocalisationSnippets(name, prefix, "", textRange)
33+
}
34+
val prefix = elementText.substring(0, i1).trimEnd()
35+
val text = elementText.substring(i1 + 1)
36+
.let { if (it.lastOrNull() == '"') it.dropLast(1) else it }
37+
val textRange = TextRange.create(i1, elementText.length)
38+
return ParadoxLocalisationSnippets(name, prefix, text, textRange)
39+
}
40+
}
41+
}

0 commit comments

Comments
 (0)