From cd4870fc2678019c9714ca860a1a39bb84bc0b7c Mon Sep 17 00:00:00 2001 From: "Yii.Guxing" Date: Mon, 22 Jul 2024 15:32:18 +0800 Subject: [PATCH 1/5] Fix changelog (cherry picked from commit 7d2ff3afca9a70593975b60c3ddfabdb29217364) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 051f6e274..7c60ff1e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,7 +39,7 @@ ## [3.5.8] (2024/04/10) - User interaction experience optimization and improvement. -- Compatible with 2024.4. +- Compatible with 2024.1. - Bug fixes. - 用户交互体验优化与改进 - 兼容 IDE 2024.1 版本 From d7b9d7f066fe0ede3abb92847ea9b334f1223903 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 22 Jul 2024 07:41:40 +0000 Subject: [PATCH 2/5] :rocket: v3.6.3 [skip ci] --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c60ff1e6..f046e8fbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## [Unreleased] +## [3.6.3] (2024/07/22) + - The GPT-4o mini is now available in the OpenAI Translator engine. - Alibaba Translate can now return the detected language. - Bug fixes. @@ -494,7 +496,8 @@ - 支持单词拆分。翻译变量名或方法名时更方便 - Bug修复 -[Unreleased]: https://github.com/YiiGuxing/TranslationPlugin/compare/v3.6.2...HEAD +[Unreleased]: https://github.com/YiiGuxing/TranslationPlugin/compare/v3.6.3...HEAD +[3.6.3]: https://github.com/YiiGuxing/TranslationPlugin/compare/v3.6.2...v3.6.3 [3.6.2]: https://github.com/YiiGuxing/TranslationPlugin/compare/v3.6.1...v3.6.2 [3.6.1]: https://github.com/YiiGuxing/TranslationPlugin/compare/v3.6.0...v3.6.1 [3.6.0]: https://github.com/YiiGuxing/TranslationPlugin/compare/v3.5.8...v3.6.0 From a76edeb25dd245febc88bb5186c6e8bf6e93429a Mon Sep 17 00:00:00 2001 From: "Yii.Guxing" Date: Tue, 23 Jul 2024 22:39:26 +0800 Subject: [PATCH 3/5] Fix async promise --- .../translate/action/SwitchEngineAction.kt | 53 ++++++++------- .../CheckGoogleNetworkStartupActivity.kt | 9 ++- .../trans/TranslationNotifications.kt | 12 +++- .../trans/deepl/DeeplSettingsDialog.kt | 47 +++++++------ .../trans/openai/ui/OpenAISettingsDialog.kt | 30 +++++---- .../trans/youdao/YoudaoSettingsDialog.kt | 18 +++-- .../translate/ui/AppKeySettingsPanel.kt | 18 +++-- .../ui/TranslationFailedComponent.kt | 67 ++++++++++--------- .../plugin/translate/ui/TranslationWidget.kt | 39 ++++++----- .../translate/ui/settings/SettingsPanel.kt | 26 ++++--- .../ui/settings/WordbookStoragePathBrowser.kt | 36 +++++----- .../ui/wordbook/WordDetailsDialog.kt | 15 +++-- .../translate/util/concurrent/Promises.kt | 6 +- .../translate/wordbook/WordBookService.kt | 54 +++++++-------- .../wordbook/WordBookToolWindowFactory.kt | 9 ++- .../plugin/translate/wordbook/WordBookView.kt | 12 ++-- 16 files changed, 261 insertions(+), 190 deletions(-) diff --git a/src/main/kotlin/cn/yiiguxing/plugin/translate/action/SwitchEngineAction.kt b/src/main/kotlin/cn/yiiguxing/plugin/translate/action/SwitchEngineAction.kt index 10c044df6..76f820488 100644 --- a/src/main/kotlin/cn/yiiguxing/plugin/translate/action/SwitchEngineAction.kt +++ b/src/main/kotlin/cn/yiiguxing/plugin/translate/action/SwitchEngineAction.kt @@ -2,10 +2,7 @@ package cn.yiiguxing.plugin.translate.action import cn.yiiguxing.plugin.translate.message import cn.yiiguxing.plugin.translate.trans.TranslateService -import cn.yiiguxing.plugin.translate.util.concurrent.errorOnUiThread -import cn.yiiguxing.plugin.translate.util.concurrent.expireWith -import cn.yiiguxing.plugin.translate.util.concurrent.finishOnUiThread -import cn.yiiguxing.plugin.translate.util.concurrent.successOnUiThread +import cn.yiiguxing.plugin.translate.util.concurrent.* import com.intellij.ide.DataManager import com.intellij.openapi.Disposable import com.intellij.openapi.actionSystem.* @@ -64,15 +61,20 @@ class SwitchEngineAction : ComboBoxAction(), DumbAware, PopupAction { isActionPerforming = true val component = e.getData(PlatformDataKeys.CONTEXT_COMPONENT) val expireDisposable = getDisposable() - runAsync { TranslationEngineActionGroup() } - .expireWith(expireDisposable) - .successOnUiThread { group -> - if (isActionPerforming && !project.isDisposed) { - val dataContext = DataManager.getInstance().getDataContext(component) - group.createActionPopup(dataContext).showCenteredInCurrentWindow(project) - } + asyncLatch { latch -> + runAsync { + latch.await() + TranslationEngineActionGroup() } - .finishOnUiThread(ModalityState.any()) { isActionPerforming = false } + .expireWith(expireDisposable) + .successOnUiThread { group -> + if (isActionPerforming && !project.isDisposed) { + val dataContext = DataManager.getInstance().getDataContext(component) + group.createActionPopup(dataContext).showCenteredInCurrentWindow(project) + } + } + .finishOnUiThread(ModalityState.any()) { isActionPerforming = false } + } } override fun createPopupActionGroup(button: JComponent): DefaultActionGroup { @@ -112,18 +114,23 @@ class SwitchEngineAction : ComboBoxAction(), DumbAware, PopupAction { isButtonActionPerforming = true val expireDisposable = getDisposable() Disposer.register(expireDisposable) { isButtonActionPerforming = false } - runAsync { TranslationEngineActionGroup() } - .expireWith(expireDisposable) - .successOnUiThread { group -> - if (isButtonActionPerforming && isShowing) { - actionGroup = group - super.fireActionPerformed(event) - } else { - isButtonActionPerforming = false - } + asyncLatch { latch -> + runAsync { + latch.await() + TranslationEngineActionGroup() } - .errorOnUiThread(ModalityState.any()) { isButtonActionPerforming = false } - .finishOnUiThread(ModalityState.any()) { disposable = null } + .expireWith(expireDisposable) + .successOnUiThread { group -> + if (isButtonActionPerforming && isShowing) { + actionGroup = group + super.fireActionPerformed(event) + } else { + isButtonActionPerforming = false + } + } + .errorOnUiThread(ModalityState.any()) { isButtonActionPerforming = false } + .finishOnUiThread(ModalityState.any()) { disposable = null } + } } } } \ No newline at end of file diff --git a/src/main/kotlin/cn/yiiguxing/plugin/translate/activity/CheckGoogleNetworkStartupActivity.kt b/src/main/kotlin/cn/yiiguxing/plugin/translate/activity/CheckGoogleNetworkStartupActivity.kt index b173bd5c1..152cee2f8 100644 --- a/src/main/kotlin/cn/yiiguxing/plugin/translate/activity/CheckGoogleNetworkStartupActivity.kt +++ b/src/main/kotlin/cn/yiiguxing/plugin/translate/activity/CheckGoogleNetworkStartupActivity.kt @@ -8,6 +8,7 @@ import cn.yiiguxing.plugin.translate.tts.TTSEngine import cn.yiiguxing.plugin.translate.ui.settings.TranslationEngine import cn.yiiguxing.plugin.translate.util.DisposableRef import cn.yiiguxing.plugin.translate.util.Notifications +import cn.yiiguxing.plugin.translate.util.concurrent.asyncLatch import cn.yiiguxing.plugin.translate.util.concurrent.successOnUiThread import com.intellij.notification.Notification import com.intellij.notification.NotificationAction @@ -38,12 +39,16 @@ class CheckGoogleNetworkStartupActivity : BaseStartupActivity() { override fun onRunActivity(project: Project) { val projectRef = DisposableRef.create(TranslationUIManager.disposable(project), project) - runAsync { TKK.testConnection() } - .successOnUiThread(projectRef, ModalityState.NON_MODAL) { p, res -> + asyncLatch { latch -> + runAsync { + latch.await() + TKK.testConnection() + }.successOnUiThread(projectRef, ModalityState.NON_MODAL) { p, res -> if (!p.isDisposed && !res) { showNotification(p) } } + } } private fun showNotification(project: Project) { diff --git a/src/main/kotlin/cn/yiiguxing/plugin/translate/trans/TranslationNotifications.kt b/src/main/kotlin/cn/yiiguxing/plugin/translate/trans/TranslationNotifications.kt index 3cb9ab612..cef88f645 100644 --- a/src/main/kotlin/cn/yiiguxing/plugin/translate/trans/TranslationNotifications.kt +++ b/src/main/kotlin/cn/yiiguxing/plugin/translate/trans/TranslationNotifications.kt @@ -3,6 +3,7 @@ package cn.yiiguxing.plugin.translate.trans import cn.yiiguxing.plugin.translate.action.TranslationEngineActionGroup import cn.yiiguxing.plugin.translate.message import cn.yiiguxing.plugin.translate.util.Notifications +import cn.yiiguxing.plugin.translate.util.concurrent.asyncLatch import cn.yiiguxing.plugin.translate.util.concurrent.finishOnUiThread import cn.yiiguxing.plugin.translate.util.concurrent.successOnUiThread import cn.yiiguxing.plugin.translate.util.e @@ -65,15 +66,20 @@ object TranslationNotifications { return } isActionPerforming = true - runAsync { TranslationEngineActionGroup() } - .successOnUiThread { group -> + asyncLatch { latch -> + runAsync { + latch.await() + TranslationEngineActionGroup() + }.successOnUiThread { group -> if (component.isShowing) { // Do not use `e.dataContext` directly because it is not an async data context. val dataContext = DataManager.getInstance().getDataContext(component) group.showActionPopup(dataContext) } + }.finishOnUiThread(ModalityState.any()) { + isActionPerforming = false } - .finishOnUiThread(ModalityState.any()) { isActionPerforming = false } + } } } } \ No newline at end of file diff --git a/src/main/kotlin/cn/yiiguxing/plugin/translate/trans/deepl/DeeplSettingsDialog.kt b/src/main/kotlin/cn/yiiguxing/plugin/translate/trans/deepl/DeeplSettingsDialog.kt index ace6203a3..07efca8ea 100644 --- a/src/main/kotlin/cn/yiiguxing/plugin/translate/trans/deepl/DeeplSettingsDialog.kt +++ b/src/main/kotlin/cn/yiiguxing/plugin/translate/trans/deepl/DeeplSettingsDialog.kt @@ -5,10 +5,7 @@ import cn.yiiguxing.plugin.translate.message import cn.yiiguxing.plugin.translate.ui.LogoHeaderPanel import cn.yiiguxing.plugin.translate.ui.UI import cn.yiiguxing.plugin.translate.util.DisposableRef -import cn.yiiguxing.plugin.translate.util.concurrent.disposeAfterProcessing -import cn.yiiguxing.plugin.translate.util.concurrent.errorOnUiThread -import cn.yiiguxing.plugin.translate.util.concurrent.expireWith -import cn.yiiguxing.plugin.translate.util.concurrent.successOnUiThread +import cn.yiiguxing.plugin.translate.util.concurrent.* import cn.yiiguxing.plugin.translate.util.getCommonMessage import cn.yiiguxing.plugin.translate.util.w import com.intellij.openapi.diagnostic.thisLogger @@ -151,16 +148,21 @@ class DeeplSettingsDialog : DialogWrapper(false) { postUsageInfo(service, null) val dialogRef = DisposableRef.create(disposable, this) - runAsync { service.getUsage() } - .expireWith(disposable) - .successOnUiThread(dialogRef) { dialog, usage -> - dialog.postUsageInfo(service, usage) + asyncLatch { latch -> + runAsync { + latch.await() + service.getUsage() } - .errorOnUiThread(dialogRef) { dialog, error -> - thisLogger().w("Failed to get usage info.", error) - dialog.postUsageInfo(service, null, error) - } - .disposeAfterProcessing(dialogRef) + .expireWith(disposable) + .successOnUiThread(dialogRef) { dialog, usage -> + dialog.postUsageInfo(service, usage) + } + .errorOnUiThread(dialogRef) { dialog, error -> + thisLogger().w("Failed to get usage info.", error) + dialog.postUsageInfo(service, null, error) + } + .disposeAfterProcessing(dialogRef) + } } private fun postUsageInfo(service: DeeplService, usage: DeeplService.Usage?, throwable: Throwable? = null) { @@ -190,14 +192,19 @@ class DeeplSettingsDialog : DialogWrapper(false) { // This is a modal dialog, so it needs to be invoked later. SwingUtilities.invokeLater { val dialogRef = DisposableRef.create(disposable, this) - runAsync { DeeplCredential.authKey to DeeplCredential.isAuthKeySet } - .expireWith(disposable) - .successOnUiThread(dialogRef) { dialog, (key, isAuthKeySet) -> - dialog.authKey = key - dialog.isOK = isAuthKeySet - dialog.doGetUsageInfo() + asyncLatch { latch -> + runAsync { + latch.await() + DeeplCredential.authKey to DeeplCredential.isAuthKeySet } - .disposeAfterProcessing(dialogRef) + .expireWith(disposable) + .successOnUiThread(dialogRef) { dialog, (key, isAuthKeySet) -> + dialog.authKey = key + dialog.isOK = isAuthKeySet + dialog.doGetUsageInfo() + } + .disposeAfterProcessing(dialogRef) + } } super.show() } diff --git a/src/main/kotlin/cn/yiiguxing/plugin/translate/trans/openai/ui/OpenAISettingsDialog.kt b/src/main/kotlin/cn/yiiguxing/plugin/translate/trans/openai/ui/OpenAISettingsDialog.kt index a6417fc12..2064908fb 100644 --- a/src/main/kotlin/cn/yiiguxing/plugin/translate/trans/openai/ui/OpenAISettingsDialog.kt +++ b/src/main/kotlin/cn/yiiguxing/plugin/translate/trans/openai/ui/OpenAISettingsDialog.kt @@ -7,6 +7,7 @@ import cn.yiiguxing.plugin.translate.trans.openai.* import cn.yiiguxing.plugin.translate.ui.selected import cn.yiiguxing.plugin.translate.ui.settings.TranslationEngine import cn.yiiguxing.plugin.translate.util.DisposableRef +import cn.yiiguxing.plugin.translate.util.concurrent.asyncLatch import cn.yiiguxing.plugin.translate.util.concurrent.disposeAfterProcessing import cn.yiiguxing.plugin.translate.util.concurrent.expireWith import cn.yiiguxing.plugin.translate.util.concurrent.successOnUiThread @@ -298,20 +299,23 @@ class OpenAISettingsDialog(private val configType: ConfigType) : DialogWrapper(f // This is a modal dialog, so it needs to be invoked later. SwingUtilities.invokeLater { val dialogRef = DisposableRef.create(disposable, this) - runAsync { - ApiKeys( - OpenAiCredentials.manager(ServiceProvider.OpenAI).credential, - OpenAiCredentials.manager(ServiceProvider.Azure).credential - ) - } - .expireWith(disposable) - .successOnUiThread(dialogRef) { dialog, apiKeys -> - dialog.apiKeys.copyFrom(apiKeys) - dialog.ui.apiKeyField.text = apiKeys[dialog.provider] - dialog.isApiKeySet = !apiKeys[dialog.provider].isNullOrEmpty() - dialog.verify() + asyncLatch { latch -> + runAsync { + latch.await() + ApiKeys( + OpenAiCredentials.manager(ServiceProvider.OpenAI).credential, + OpenAiCredentials.manager(ServiceProvider.Azure).credential + ) } - .disposeAfterProcessing(dialogRef) + .expireWith(disposable) + .successOnUiThread(dialogRef) { dialog, apiKeys -> + dialog.apiKeys.copyFrom(apiKeys) + dialog.ui.apiKeyField.text = apiKeys[dialog.provider] + dialog.isApiKeySet = !apiKeys[dialog.provider].isNullOrEmpty() + dialog.verify() + } + .disposeAfterProcessing(dialogRef) + } } super.show() diff --git a/src/main/kotlin/cn/yiiguxing/plugin/translate/trans/youdao/YoudaoSettingsDialog.kt b/src/main/kotlin/cn/yiiguxing/plugin/translate/trans/youdao/YoudaoSettingsDialog.kt index 89bafefc3..a06d78c67 100644 --- a/src/main/kotlin/cn/yiiguxing/plugin/translate/trans/youdao/YoudaoSettingsDialog.kt +++ b/src/main/kotlin/cn/yiiguxing/plugin/translate/trans/youdao/YoudaoSettingsDialog.kt @@ -9,6 +9,7 @@ import cn.yiiguxing.plugin.translate.ui.UI import cn.yiiguxing.plugin.translate.ui.selected import cn.yiiguxing.plugin.translate.ui.settings.TranslationEngine import cn.yiiguxing.plugin.translate.util.DisposableRef +import cn.yiiguxing.plugin.translate.util.concurrent.asyncLatch import cn.yiiguxing.plugin.translate.util.concurrent.disposeAfterProcessing import cn.yiiguxing.plugin.translate.util.concurrent.expireWith import cn.yiiguxing.plugin.translate.util.concurrent.successOnUiThread @@ -122,13 +123,18 @@ class YoudaoSettingsDialog : DialogWrapper(true) { // This is a modal dialog, so it needs to be invoked later. SwingUtilities.invokeLater { val dialogRef = DisposableRef.create(disposable, this) - runAsync { credentialSettings.getAppKey() to credentialSettings.isAppKeySet } - .expireWith(disposable) - .successOnUiThread(dialogRef) { dialog, (appKey, isAppKeySet) -> - dialog.appKey = appKey - dialog.isAppKeySet = isAppKeySet + asyncLatch { latch -> + runAsync { + latch.await() + credentialSettings.getAppKey() to credentialSettings.isAppKeySet } - .disposeAfterProcessing(dialogRef) + .expireWith(disposable) + .successOnUiThread(dialogRef) { dialog, (appKey, isAppKeySet) -> + dialog.appKey = appKey + dialog.isAppKeySet = isAppKeySet + } + .disposeAfterProcessing(dialogRef) + } } super.show() } diff --git a/src/main/kotlin/cn/yiiguxing/plugin/translate/ui/AppKeySettingsPanel.kt b/src/main/kotlin/cn/yiiguxing/plugin/translate/ui/AppKeySettingsPanel.kt index cf64500b4..1bd4e995a 100644 --- a/src/main/kotlin/cn/yiiguxing/plugin/translate/ui/AppKeySettingsPanel.kt +++ b/src/main/kotlin/cn/yiiguxing/plugin/translate/ui/AppKeySettingsPanel.kt @@ -6,6 +6,7 @@ import cn.yiiguxing.plugin.translate.ui.UI.fillX import cn.yiiguxing.plugin.translate.ui.UI.migLayout import cn.yiiguxing.plugin.translate.ui.UI.wrap import cn.yiiguxing.plugin.translate.util.DisposableRef +import cn.yiiguxing.plugin.translate.util.concurrent.asyncLatch import cn.yiiguxing.plugin.translate.util.concurrent.disposeAfterProcessing import cn.yiiguxing.plugin.translate.util.concurrent.expireWith import cn.yiiguxing.plugin.translate.util.concurrent.successOnUiThread @@ -67,13 +68,18 @@ class AppKeySettingsPanel( appIdField.text = appKeySettings.appId appKey = "" val ref = DisposableRef.create(this, this) - runAsync { appKeySettings.getAppKey() to appKeySettings.isAppKeySet } - .expireWith(this) - .successOnUiThread(ref) { panel, (key, isAppKeySet) -> - panel.appKey = key - panel.isAppKeySet = isAppKeySet + asyncLatch { latch -> + runAsync { + latch.await() + appKeySettings.getAppKey() to appKeySettings.isAppKeySet } - .disposeAfterProcessing(ref) + .expireWith(this) + .successOnUiThread(ref) { panel, (key, isAppKeySet) -> + panel.appKey = key + panel.isAppKeySet = isAppKeySet + } + .disposeAfterProcessing(ref) + } } fun apply() { diff --git a/src/main/kotlin/cn/yiiguxing/plugin/translate/ui/TranslationFailedComponent.kt b/src/main/kotlin/cn/yiiguxing/plugin/translate/ui/TranslationFailedComponent.kt index 57437fbe3..d327ec32e 100644 --- a/src/main/kotlin/cn/yiiguxing/plugin/translate/ui/TranslationFailedComponent.kt +++ b/src/main/kotlin/cn/yiiguxing/plugin/translate/ui/TranslationFailedComponent.kt @@ -4,10 +4,7 @@ import cn.yiiguxing.plugin.translate.action.TranslationEngineActionGroup import cn.yiiguxing.plugin.translate.message import cn.yiiguxing.plugin.translate.trans.TranslateException import cn.yiiguxing.plugin.translate.util.DisposableRef -import cn.yiiguxing.plugin.translate.util.concurrent.disposeAfterProcessing -import cn.yiiguxing.plugin.translate.util.concurrent.expireWith -import cn.yiiguxing.plugin.translate.util.concurrent.finishOnUiThread -import cn.yiiguxing.plugin.translate.util.concurrent.successOnUiThread +import cn.yiiguxing.plugin.translate.util.concurrent.* import com.intellij.icons.AllIcons import com.intellij.ide.DataManager import com.intellij.openapi.Disposable @@ -106,36 +103,42 @@ class TranslationFailedComponent : JPanel(), Disposable { isLoadingTranslationEngines = true val widgetRef = DisposableRef.create(this, this) - runAsync { TranslationEngineActionGroup() } - .expireWith(this) - .successOnUiThread(widgetRef) { widget, group -> - val button = widget.optionButton.takeIf { it.isShowing } ?: return@successOnUiThread - var offsetLeft: Int - var offsetRight: Int - var offsetBottom: Int - if (button.componentCount > 0) { - val first = (button.getComponent(0) as? JComponent)?.insets ?: button.insets - val last = (button.getComponent(button.componentCount - 1) as? JComponent)?.insets ?: button.insets - offsetLeft = first.left - offsetRight = last.right - offsetBottom = first.right - } else { - button.insets.let { - offsetLeft = it.left - offsetRight = it.right - offsetBottom = it.bottom + asyncLatch { latch -> + runAsync { + latch.await() + TranslationEngineActionGroup() + } + .expireWith(this) + .successOnUiThread(widgetRef) { widget, group -> + val button = widget.optionButton.takeIf { it.isShowing } ?: return@successOnUiThread + var offsetLeft: Int + var offsetRight: Int + var offsetBottom: Int + if (button.componentCount > 0) { + val first = (button.getComponent(0) as? JComponent)?.insets ?: button.insets + val last = (button.getComponent(button.componentCount - 1) as? JComponent)?.insets + ?: button.insets + offsetLeft = first.left + offsetRight = last.right + offsetBottom = first.right + } else { + button.insets.let { + offsetLeft = it.left + offsetRight = it.right + offsetBottom = it.bottom + } } - } - val dataContext = DataManager.getInstance().getDataContext(button) - group.createActionPopup(dataContext) - .apply { minimumSize = Dimension(button.width - offsetLeft - offsetRight, 1) } - .showBelow(button, offsetLeft, offsetBottom) - } - .finishOnUiThread(widgetRef, ModalityState.any()) { widget, _ -> - widget.isLoadingTranslationEngines = false - } - .disposeAfterProcessing(widgetRef) + val dataContext = DataManager.getInstance().getDataContext(button) + group.createActionPopup(dataContext) + .apply { minimumSize = Dimension(button.width - offsetLeft - offsetRight, 1) } + .showBelow(button, offsetLeft, offsetBottom) + } + .finishOnUiThread(widgetRef, ModalityState.any()) { widget, _ -> + widget.isLoadingTranslationEngines = false + } + .disposeAfterProcessing(widgetRef) + } } fun onRetry(handler: () -> Unit) { diff --git a/src/main/kotlin/cn/yiiguxing/plugin/translate/ui/TranslationWidget.kt b/src/main/kotlin/cn/yiiguxing/plugin/translate/ui/TranslationWidget.kt index e65bb01c9..6723f45c3 100644 --- a/src/main/kotlin/cn/yiiguxing/plugin/translate/ui/TranslationWidget.kt +++ b/src/main/kotlin/cn/yiiguxing/plugin/translate/ui/TranslationWidget.kt @@ -11,10 +11,7 @@ import cn.yiiguxing.plugin.translate.trans.TranslateService import cn.yiiguxing.plugin.translate.ui.settings.TranslationEngine import cn.yiiguxing.plugin.translate.update.UpdateListener import cn.yiiguxing.plugin.translate.util.DisposableRef -import cn.yiiguxing.plugin.translate.util.concurrent.disposeAfterProcessing -import cn.yiiguxing.plugin.translate.util.concurrent.expireWith -import cn.yiiguxing.plugin.translate.util.concurrent.finishOnUiThread -import cn.yiiguxing.plugin.translate.util.concurrent.successOnUiThread +import cn.yiiguxing.plugin.translate.util.concurrent.* import cn.yiiguxing.plugin.translate.util.invokeLaterIfNeeded import com.intellij.ide.DataManager import com.intellij.openapi.application.ModalityState @@ -136,21 +133,27 @@ class TranslationWidget(private val project: Project) : WithIconAndArrows(), Ico isLoadingTranslationEngines = true val widgetRef = DisposableRef.create(this, this) - runAsync { TranslationEngineActionGroup() } - .expireWith(this) - .successOnUiThread(widgetRef) { widget, group -> - val context = DataManager.getInstance().getDataContext(widget) - val popup = group.createActionPopup(context) - val at = Point(0, -popup.content.preferredSize.height) - popup.show(RelativePoint(widget, at)) - } - .onError { - logger().warn("Failed to show translation engines popup.", it) - } - .finishOnUiThread(widgetRef, ModalityState.any()) { widget, _ -> - widget.isLoadingTranslationEngines = false + + asyncLatch { latch -> + runAsync { + latch.await() + TranslationEngineActionGroup() } - .disposeAfterProcessing(widgetRef) + .expireWith(this) + .successOnUiThread(widgetRef) { widget, group -> + val context = DataManager.getInstance().getDataContext(widget) + val popup = group.createActionPopup(context) + val at = Point(0, -popup.content.preferredSize.height) + popup.show(RelativePoint(widget, at)) + } + .onError { + logger().warn("Failed to show translation engines popup.", it) + } + .finishOnUiThread(widgetRef, ModalityState.any()) { widget, _ -> + widget.isLoadingTranslationEngines = false + } + .disposeAfterProcessing(widgetRef) + } } override fun dispose() { diff --git a/src/main/kotlin/cn/yiiguxing/plugin/translate/ui/settings/SettingsPanel.kt b/src/main/kotlin/cn/yiiguxing/plugin/translate/ui/settings/SettingsPanel.kt index ce5ffad3f..3979b1e07 100644 --- a/src/main/kotlin/cn/yiiguxing/plugin/translate/ui/settings/SettingsPanel.kt +++ b/src/main/kotlin/cn/yiiguxing/plugin/translate/ui/settings/SettingsPanel.kt @@ -9,6 +9,7 @@ import cn.yiiguxing.plugin.translate.ui.selected import cn.yiiguxing.plugin.translate.util.ByteSize import cn.yiiguxing.plugin.translate.util.DisposableRef import cn.yiiguxing.plugin.translate.util.SelectionMode +import cn.yiiguxing.plugin.translate.util.concurrent.asyncLatch import cn.yiiguxing.plugin.translate.util.concurrent.finishOnUiThread import cn.yiiguxing.plugin.translate.util.concurrent.successOnUiThread import cn.yiiguxing.plugin.translate.wordbook.WordBookService @@ -85,21 +86,28 @@ class SettingsPanel( } isClearing = true - runAsync { - with(CacheService.getInstance()) { - evictAllDiskCaches() - getDiskCacheSize() + asyncLatch { latch -> + runAsync { + latch.await() + with(CacheService.getInstance()) { + evictAllDiskCaches() + getDiskCacheSize() + } + }.finishOnUiThread(labelRef) { label, size -> + isClearing = false + label.text = ByteSize.format(size ?: 0L) } - }.finishOnUiThread(labelRef) { label, size -> - isClearing = false - label.text = ByteSize.format(size ?: 0L) } } - runAsync { CacheService.getInstance().getDiskCacheSize() } - .successOnUiThread(labelRef) { label, size -> + asyncLatch { latch -> + runAsync { + latch.await() + CacheService.getInstance().getDiskCacheSize() + }.successOnUiThread(labelRef) { label, size -> label.text = ByteSize.format(size) } + } } private fun initSupport() { diff --git a/src/main/kotlin/cn/yiiguxing/plugin/translate/ui/settings/WordbookStoragePathBrowser.kt b/src/main/kotlin/cn/yiiguxing/plugin/translate/ui/settings/WordbookStoragePathBrowser.kt index 6291c3c04..80c88f617 100644 --- a/src/main/kotlin/cn/yiiguxing/plugin/translate/ui/settings/WordbookStoragePathBrowser.kt +++ b/src/main/kotlin/cn/yiiguxing/plugin/translate/ui/settings/WordbookStoragePathBrowser.kt @@ -3,6 +3,7 @@ package cn.yiiguxing.plugin.translate.ui.settings import cn.yiiguxing.plugin.translate.Settings import cn.yiiguxing.plugin.translate.TranslationStorages import cn.yiiguxing.plugin.translate.message +import cn.yiiguxing.plugin.translate.util.concurrent.asyncLatch import cn.yiiguxing.plugin.translate.util.invokeLater import cn.yiiguxing.plugin.translate.wordbook.WordBookService import com.intellij.openapi.fileChooser.FileChooserDescriptor @@ -91,24 +92,27 @@ internal class WordbookStoragePathBrowser(val settings: Settings) : TextBrowseFo return } - runAsync { - Files.createDirectories(toPath.parent) - Files.copy(fromPath, toPath, StandardCopyOption.REPLACE_EXISTING) - }.onError { - invokeLater { - val retry = MessageDialogBuilder - .okCancel( - message("settings.wordbook.alert.title.move.storage.file"), - message("settings.wordbook.alert.message.failed.to.move.storage.file", it.message ?: "") - ) - .icon(UIUtil.getErrorIcon()) - .ask(null as Project?) - if (retry) { - moveWordbookStorageFile(fromPath, toPath, onSuccess) + asyncLatch { latch -> + runAsync { + latch.await() + Files.createDirectories(toPath.parent) + Files.copy(fromPath, toPath, StandardCopyOption.REPLACE_EXISTING) + }.onError { + invokeLater { + val retry = MessageDialogBuilder + .okCancel( + message("settings.wordbook.alert.title.move.storage.file"), + message("settings.wordbook.alert.message.failed.to.move.storage.file", it.message ?: "") + ) + .icon(UIUtil.getErrorIcon()) + .ask(null as Project?) + if (retry) { + moveWordbookStorageFile(fromPath, toPath, onSuccess) + } } + }.onSuccess { + invokeLater(onSuccess) } - }.onSuccess { - invokeLater(onSuccess) } } } diff --git a/src/main/kotlin/cn/yiiguxing/plugin/translate/ui/wordbook/WordDetailsDialog.kt b/src/main/kotlin/cn/yiiguxing/plugin/translate/ui/wordbook/WordDetailsDialog.kt index 2b611c3a1..9991052ff 100644 --- a/src/main/kotlin/cn/yiiguxing/plugin/translate/ui/wordbook/WordDetailsDialog.kt +++ b/src/main/kotlin/cn/yiiguxing/plugin/translate/ui/wordbook/WordDetailsDialog.kt @@ -2,6 +2,7 @@ package cn.yiiguxing.plugin.translate.ui.wordbook import cn.yiiguxing.plugin.translate.message import cn.yiiguxing.plugin.translate.ui.Popups +import cn.yiiguxing.plugin.translate.util.concurrent.asyncLatch import cn.yiiguxing.plugin.translate.util.e import cn.yiiguxing.plugin.translate.util.invokeLater import cn.yiiguxing.plugin.translate.wordbook.* @@ -158,22 +159,24 @@ class WordDetailsDialog( val modalityState = ModalityState.current() val dialogRef = WeakReference(this) val expired = Condition { dialogRef.get()?.isDisposed ?: true } - runAsync { WordBookService.getInstance().updateWord(newWord) } - .onSuccess { updated -> + asyncLatch { latch -> + runAsync { + latch.await() + WordBookService.getInstance().updateWord(newWord) + }.onSuccess { updated -> if (updated) invokeLater(modalityState, expired) { dialogRef.get()?.onEditingSaved(newWord) } - } - .onError { error -> + }.onError { error -> invokeLater(modalityState, expired) { dialogRef.get()?.onEditError(error) } - } - .onProcessed { + }.onProcessed { invokeLater(modalityState, expired) { dialogRef.get()?.saveAction?.isEnabled = true } } + } } private fun onEditingSaved(newWord: WordBookItem) { diff --git a/src/main/kotlin/cn/yiiguxing/plugin/translate/util/concurrent/Promises.kt b/src/main/kotlin/cn/yiiguxing/plugin/translate/util/concurrent/Promises.kt index 4ceb8a8d4..99c119172 100644 --- a/src/main/kotlin/cn/yiiguxing/plugin/translate/util/concurrent/Promises.kt +++ b/src/main/kotlin/cn/yiiguxing/plugin/translate/util/concurrent/Promises.kt @@ -3,7 +3,7 @@ package cn.yiiguxing.plugin.translate.util.concurrent import cn.yiiguxing.plugin.translate.util.DisposableRef -import cn.yiiguxing.plugin.translate.util.invokeLater +import cn.yiiguxing.plugin.translate.util.invokeAndWait import com.intellij.openapi.Disposable import com.intellij.openapi.application.ModalityState import com.intellij.openapi.project.Project @@ -100,7 +100,7 @@ internal inline fun Promise.onUiThread( crossinline uiThreadAction: (V) -> Unit ): Promise { return fn(Consumer { result -> - invokeLater(modalityState) { uiThreadAction(result) } + invokeAndWait(modalityState) { uiThreadAction(result) } }) } @@ -113,7 +113,7 @@ internal inline fun Promise.onUiThread( return expireWith(disposableRef) .fn(Consumer { result -> if (disposableRef.get() != null && !Disposer.isDisposed(disposableRef)) { - invokeLater(modalityState) { + invokeAndWait(modalityState) { disposableRef.get()?.let { uiThreadAction(it, result) } } } diff --git a/src/main/kotlin/cn/yiiguxing/plugin/translate/wordbook/WordBookService.kt b/src/main/kotlin/cn/yiiguxing/plugin/translate/wordbook/WordBookService.kt index 3066e5cda..5320d243e 100644 --- a/src/main/kotlin/cn/yiiguxing/plugin/translate/wordbook/WordBookService.kt +++ b/src/main/kotlin/cn/yiiguxing/plugin/translate/wordbook/WordBookService.kt @@ -8,6 +8,7 @@ import cn.yiiguxing.plugin.translate.TranslationStorages import cn.yiiguxing.plugin.translate.message import cn.yiiguxing.plugin.translate.trans.Lang import cn.yiiguxing.plugin.translate.util.* +import cn.yiiguxing.plugin.translate.util.concurrent.asyncLatch import cn.yiiguxing.plugin.translate.wordbook.WordBookState.* import com.intellij.openapi.Disposable import com.intellij.openapi.application.ModalityState @@ -31,7 +32,6 @@ import java.nio.file.StandardCopyOption import java.sql.Driver import java.sql.ResultSet import java.sql.SQLException -import java.util.concurrent.CountDownLatch import java.util.concurrent.atomic.AtomicBoolean import javax.sql.DataSource @@ -116,36 +116,38 @@ class WordBookService : Disposable { } } - runAsync { - val newDbFile = newPath?.takeIf { it.isNotBlank() } - ?.let { getStorageFile(Paths.get(it)) } - ?: getStorageFile(TranslationStorages.DATA_DIRECTORY) - Files.createDirectories(newDbFile.parent) - - val runner = createRunner(newDbFile) - synchronized(this@WordBookService) { - queryRunner = runner - } + asyncLatch { latch -> + runAsync { + latch.await() + val newDbFile = newPath?.takeIf { it.isNotBlank() } + ?.let { getStorageFile(Paths.get(it)) } + ?: getStorageFile(TranslationStorages.DATA_DIRECTORY) + Files.createDirectories(newDbFile.parent) + + val runner = createRunner(newDbFile) + synchronized(this@WordBookService) { + queryRunner = runner + } - invokeLater(ModalityState.any()) { wordBookPublisher.onStoragePathChanged(this@WordBookService) } - }.onError { error -> - nextState(INITIALIZATION_ERROR) - val errorMsg = (error as? SQLException) - ?.let { WordBookErrorCode[it.errorCode].reason } - ?: error.message ?: "" - val title = message("wordbook.service.notification.title") - val message = - message("wordbook.service.notification.message.failed.to.switch.storage.path", errorMsg) - invokeLater(ModalityState.NON_MODAL) { - LOGGER.w("Failed to switch storage path", error) - Notifications.showErrorNotification(title, message) + invokeLater(ModalityState.any()) { wordBookPublisher.onStoragePathChanged(this@WordBookService) } + }.onError { error -> + nextState(INITIALIZATION_ERROR) + val errorMsg = (error as? SQLException) + ?.let { WordBookErrorCode[it.errorCode].reason } + ?: error.message ?: "" + val title = message("wordbook.service.notification.title") + val message = + message("wordbook.service.notification.message.failed.to.switch.storage.path", errorMsg) + invokeLater(ModalityState.NON_MODAL) { + LOGGER.w("Failed to switch storage path", error) + Notifications.showErrorNotification(title, message) + } } } } fun asyncInitialize() { - val latch = CountDownLatch(1) - try { + asyncLatch { latch -> runAsync { // 确保外部各种回调都准备好了再执行后台任务 latch.await() @@ -164,8 +166,6 @@ class WordBookService : Disposable { LOGGER.w("Wordbook initialization failed", it) nextState(INITIALIZATION_ERROR) } - } finally { - latch.countDown() } } diff --git a/src/main/kotlin/cn/yiiguxing/plugin/translate/wordbook/WordBookToolWindowFactory.kt b/src/main/kotlin/cn/yiiguxing/plugin/translate/wordbook/WordBookToolWindowFactory.kt index bc42e803c..21e6bc7c0 100644 --- a/src/main/kotlin/cn/yiiguxing/plugin/translate/wordbook/WordBookToolWindowFactory.kt +++ b/src/main/kotlin/cn/yiiguxing/plugin/translate/wordbook/WordBookToolWindowFactory.kt @@ -6,6 +6,7 @@ import cn.yiiguxing.plugin.translate.adaptedMessage import cn.yiiguxing.plugin.translate.message import cn.yiiguxing.plugin.translate.ui.settings.TranslationConfigurable import cn.yiiguxing.plugin.translate.util.* +import cn.yiiguxing.plugin.translate.util.concurrent.asyncLatch import cn.yiiguxing.plugin.translate.util.concurrent.successOnUiThread import com.intellij.ide.util.PropertiesComponent import com.intellij.notification.NotificationAction @@ -148,12 +149,16 @@ internal class WordBookToolWindowFactoryImpl : WordBookToolWindowFactory { } private fun DisposableRef.updateVisible() { - runAsync { with(WordBookService.getInstance()) { isInitialized && hasWords() } } - .successOnUiThread(this, ModalityState.NON_MODAL) { toolWindow, available -> + asyncLatch { latch -> + runAsync { + latch.await() + with(WordBookService.getInstance()) { isInitialized && hasWords() } + }.successOnUiThread(this, ModalityState.NON_MODAL) { toolWindow, available -> if (available) { toolWindow.showStripeButton() } } + } } override fun shouldBeAvailable(project: Project): Boolean = true diff --git a/src/main/kotlin/cn/yiiguxing/plugin/translate/wordbook/WordBookView.kt b/src/main/kotlin/cn/yiiguxing/plugin/translate/wordbook/WordBookView.kt index fda5ca916..5f898cd32 100644 --- a/src/main/kotlin/cn/yiiguxing/plugin/translate/wordbook/WordBookView.kt +++ b/src/main/kotlin/cn/yiiguxing/plugin/translate/wordbook/WordBookView.kt @@ -6,6 +6,7 @@ import cn.yiiguxing.plugin.translate.ui.Popups import cn.yiiguxing.plugin.translate.ui.wordbook.WordBookWindowComponent import cn.yiiguxing.plugin.translate.ui.wordbook.WordDetailsDialog import cn.yiiguxing.plugin.translate.util.* +import cn.yiiguxing.plugin.translate.util.concurrent.asyncLatch import cn.yiiguxing.plugin.translate.wordbook.exports.WordBookExporter import com.intellij.icons.AllIcons import com.intellij.openapi.Disposable @@ -199,18 +200,21 @@ class WordBookView { isLoading = true val modalityState = ModalityState.current() - runAsync { service.getWords() } - .onSuccess { newWords -> + asyncLatch { latch -> + runAsync { + latch.await() + service.getWords() + }.onSuccess { newWords -> invokeLater(modalityState) { words.clear() words.addAll(newWords) notifyWordsChanged() publisher.onWordBookRefreshed(newWords) } - } - .onProcessed { + }.onProcessed { invokeLater(modalityState) { isLoading = false } } + } } private fun notifyWordsChanged() { From 56f09d09b791bdae1509e4cfdfccf5c787061d23 Mon Sep 17 00:00:00 2001 From: "Yii.Guxing" Date: Tue, 23 Jul 2024 22:39:48 +0800 Subject: [PATCH 4/5] Update changelog --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f046e8fbd..2a01fadc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ ## [Unreleased] +- The GPT-4o mini is now available in the OpenAI Translator engine. +- Alibaba Translate can now return the detected language. +- Fixed the issue where clicking on the status bar widget had no effect. +- GPT-4o mini 现已在 OpenAI 翻译引擎中可用 +- 阿里翻译现在能返回检测到的语言 +- 修复了状态栏徽标点击无效的问题 + ## [3.6.3] (2024/07/22) - The GPT-4o mini is now available in the OpenAI Translator engine. From 8cd8fffab176173e8d13f13f85bd7e2311cad745 Mon Sep 17 00:00:00 2001 From: "Yii.Guxing" Date: Tue, 23 Jul 2024 22:40:02 +0800 Subject: [PATCH 5/5] :triangular_flag_on_post: v3.6.4 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index dfd5591be..ed2ad850b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ pluginGroup = cn.yiiguxing.plugin.translate pluginRepositoryUrl = https://github.com/YiiGuxing/TranslationPlugin # SemVer format -> https://semver.org -pluginMajorVersion = 3.6.3 +pluginMajorVersion = 3.6.4 pluginPreReleaseVersion = pluginBuildMetadata = autoSnapshotVersion = true