From fedbe11fd266ff19e981f3d74bed33f49da231f3 Mon Sep 17 00:00:00 2001 From: Carl-Robert Linnupuu Date: Thu, 9 May 2024 13:03:38 +0300 Subject: [PATCH] fix: long-running tasks on EDT when initializing forms --- .../anthropic/AnthropicSettingsForm.java | 7 +++- .../service/azure/AzureSettingsForm.java | 13 ++++-- .../form/LlamaServerPreferencesForm.java | 41 +++++++++++-------- .../service/openai/OpenAISettingsForm.java | 8 +++- .../settings/service/you/YouSettingsForm.java | 7 +++- .../codegpt/credentials/CredentialsStore.kt | 4 +- .../service/codegpt/CodeGPTServiceForm.kt | 10 ++++- .../service/custom/form/CustomServiceForm.kt | 6 ++- .../service/google/GoogleSettingsForm.kt | 15 +++---- 9 files changed, 75 insertions(+), 36 deletions(-) diff --git a/src/main/java/ee/carlrobert/codegpt/settings/service/anthropic/AnthropicSettingsForm.java b/src/main/java/ee/carlrobert/codegpt/settings/service/anthropic/AnthropicSettingsForm.java index 999128fb..ed11ec5c 100644 --- a/src/main/java/ee/carlrobert/codegpt/settings/service/anthropic/AnthropicSettingsForm.java +++ b/src/main/java/ee/carlrobert/codegpt/settings/service/anthropic/AnthropicSettingsForm.java @@ -2,6 +2,7 @@ package ee.carlrobert.codegpt.settings.service.anthropic; import static ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.ANTHROPIC_API_KEY; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.ui.components.JBPasswordField; import com.intellij.ui.components.JBTextField; import com.intellij.util.ui.FormBuilder; @@ -10,6 +11,7 @@ import ee.carlrobert.codegpt.CodeGPTBundle; import ee.carlrobert.codegpt.credentials.CredentialsStore; import ee.carlrobert.codegpt.ui.UIUtil; import javax.swing.JPanel; +import javax.swing.SwingUtilities; import org.jetbrains.annotations.Nullable; public class AnthropicSettingsForm { @@ -21,7 +23,10 @@ public class AnthropicSettingsForm { public AnthropicSettingsForm(AnthropicSettingsState settings) { apiKeyField = new JBPasswordField(); apiKeyField.setColumns(30); - apiKeyField.setText(CredentialsStore.getCredential(ANTHROPIC_API_KEY)); + ApplicationManager.getApplication().executeOnPooledThread(() -> { + var apiKey = CredentialsStore.getCredential(ANTHROPIC_API_KEY); + SwingUtilities.invokeLater(() -> apiKeyField.setText(apiKey)); + }); apiVersionField = new JBTextField(settings.getApiVersion(), 35); modelField = new JBTextField(settings.getModel(), 35); } diff --git a/src/main/java/ee/carlrobert/codegpt/settings/service/azure/AzureSettingsForm.java b/src/main/java/ee/carlrobert/codegpt/settings/service/azure/AzureSettingsForm.java index 1b2b4391..10f443a2 100644 --- a/src/main/java/ee/carlrobert/codegpt/settings/service/azure/AzureSettingsForm.java +++ b/src/main/java/ee/carlrobert/codegpt/settings/service/azure/AzureSettingsForm.java @@ -2,6 +2,7 @@ package ee.carlrobert.codegpt.settings.service.azure; import static ee.carlrobert.codegpt.ui.UIUtil.withEmptyLeftBorder; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.ui.TitledSeparator; import com.intellij.ui.components.JBPasswordField; import com.intellij.ui.components.JBRadioButton; @@ -15,6 +16,7 @@ import java.util.List; import java.util.Map; import javax.swing.ButtonGroup; import javax.swing.JPanel; +import javax.swing.SwingUtilities; import org.jetbrains.annotations.Nullable; public class AzureSettingsForm { @@ -38,15 +40,20 @@ public class AzureSettingsForm { settings.isUseAzureActiveDirectoryAuthentication()); azureApiKeyField = new JBPasswordField(); azureApiKeyField.setColumns(30); - azureApiKeyField.setText(CredentialsStore.getCredential(CredentialKey.AZURE_OPENAI_API_KEY)); + ApplicationManager.getApplication().executeOnPooledThread(() -> { + var apiKey = CredentialsStore.getCredential(CredentialKey.AZURE_OPENAI_API_KEY); + SwingUtilities.invokeLater(() -> azureApiKeyField.setText(apiKey)); + }); azureApiKeyFieldPanel = UI.PanelFactory.panel(azureApiKeyField) .withLabel(CodeGPTBundle.get("settingsConfigurable.shared.apiKey.label")) .resizeX(false) .createPanel(); azureActiveDirectoryTokenField = new JBPasswordField(); azureActiveDirectoryTokenField.setColumns(30); - azureActiveDirectoryTokenField.setText( - CredentialsStore.getCredential(CredentialKey.AZURE_ACTIVE_DIRECTORY_TOKEN)); + ApplicationManager.getApplication().executeOnPooledThread(() -> { + var apiKey = CredentialsStore.getCredential(CredentialKey.AZURE_ACTIVE_DIRECTORY_TOKEN); + SwingUtilities.invokeLater(() -> azureActiveDirectoryTokenField.setText(apiKey)); + }); azureActiveDirectoryTokenFieldPanel = UI.PanelFactory.panel(azureActiveDirectoryTokenField) .withLabel(CodeGPTBundle.get("settingsConfigurable.service.azure.bearerToken.label")) .resizeX(false) diff --git a/src/main/java/ee/carlrobert/codegpt/settings/service/llama/form/LlamaServerPreferencesForm.java b/src/main/java/ee/carlrobert/codegpt/settings/service/llama/form/LlamaServerPreferencesForm.java index 00c2e822..172a302b 100644 --- a/src/main/java/ee/carlrobert/codegpt/settings/service/llama/form/LlamaServerPreferencesForm.java +++ b/src/main/java/ee/carlrobert/codegpt/settings/service/llama/form/LlamaServerPreferencesForm.java @@ -28,6 +28,7 @@ import ee.carlrobert.codegpt.completions.llama.LlamaServerAgent; import ee.carlrobert.codegpt.completions.llama.LlamaServerStartupParams; import ee.carlrobert.codegpt.completions.llama.PromptTemplate; import ee.carlrobert.codegpt.credentials.CredentialsStore; +import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey; import ee.carlrobert.codegpt.settings.service.llama.LlamaSettingsState; import ee.carlrobert.codegpt.ui.OverlayUtil; import ee.carlrobert.codegpt.ui.UIUtil; @@ -40,6 +41,7 @@ import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; import org.jetbrains.annotations.Nullable; public class LlamaServerPreferencesForm { @@ -86,7 +88,10 @@ public class LlamaServerPreferencesForm { baseHostField = new JBTextField(settings.getBaseHost(), 30); apiKeyField = new JBPasswordField(); apiKeyField.setColumns(30); - apiKeyField.setText(CredentialsStore.getCredential(LLAMA_API_KEY)); + ApplicationManager.getApplication().executeOnPooledThread(() -> { + var apiKey = CredentialsStore.getCredential(CredentialKey.LLAMA_API_KEY); + SwingUtilities.invokeLater(() -> apiKeyField.setText(apiKey)); + }); llamaModelPreferencesForm = new LlamaModelPreferencesForm(); runLocalServerRadioButton = new JBRadioButton("Run local server", @@ -189,17 +194,17 @@ public class LlamaServerPreferencesForm { createComment("settingsConfigurable.service.llama.threads.comment")) .addLabeledComponent( CodeGPTBundle.get("settingsConfigurable.service.llama.additionalParameters.label"), - additionalParametersField) - .addComponentToRightColumn( - createComment( - "settingsConfigurable.service.llama.additionalParameters.comment")) - .addLabeledComponent( - CodeGPTBundle.get( - "settingsConfigurable.service.llama.additionalBuildParameters.label"), - additionalBuildParametersField) - .addComponentToRightColumn( - createComment( - "settingsConfigurable.service.llama.additionalBuildParameters.comment")) + additionalParametersField) + .addComponentToRightColumn( + createComment( + "settingsConfigurable.service.llama.additionalParameters.comment")) + .addLabeledComponent( + CodeGPTBundle.get( + "settingsConfigurable.service.llama.additionalBuildParameters.label"), + additionalBuildParametersField) + .addComponentToRightColumn( + createComment( + "settingsConfigurable.service.llama.additionalBuildParameters.comment")) .addVerticalGap(4) .addComponentFillVertically(new JPanel(), 0) .getPanel())) @@ -354,9 +359,9 @@ public class LlamaServerPreferencesForm { public List getListOfAdditionalParameters() { return Arrays.stream(additionalParametersField.getText().split(",")) - .map(String::trim) - .filter(s -> !s.isBlank()) - .toList(); + .map(String::trim) + .filter(s -> !s.isBlank()) + .toList(); } public String getAdditionalBuildParameters() { @@ -365,9 +370,9 @@ public class LlamaServerPreferencesForm { public List getListOfAdditionalBuildParameters() { return Arrays.stream(additionalBuildParametersField.getText().split(",")) - .map(String::trim) - .filter(s -> !s.isBlank()) - .toList(); + .map(String::trim) + .filter(s -> !s.isBlank()) + .toList(); } public PromptTemplate getPromptTemplate() { diff --git a/src/main/java/ee/carlrobert/codegpt/settings/service/openai/OpenAISettingsForm.java b/src/main/java/ee/carlrobert/codegpt/settings/service/openai/OpenAISettingsForm.java index e3663bb9..ac65ef41 100644 --- a/src/main/java/ee/carlrobert/codegpt/settings/service/openai/OpenAISettingsForm.java +++ b/src/main/java/ee/carlrobert/codegpt/settings/service/openai/OpenAISettingsForm.java @@ -3,6 +3,7 @@ package ee.carlrobert.codegpt.settings.service.openai; import static ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.OPENAI_API_KEY; import static ee.carlrobert.codegpt.ui.UIUtil.withEmptyLeftBorder; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.ui.ComboBox; import com.intellij.ui.EnumComboBoxModel; import com.intellij.ui.TitledSeparator; @@ -12,10 +13,12 @@ import com.intellij.util.ui.FormBuilder; import com.intellij.util.ui.UI; import ee.carlrobert.codegpt.CodeGPTBundle; import ee.carlrobert.codegpt.credentials.CredentialsStore; +import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey; import ee.carlrobert.codegpt.settings.service.CodeCompletionConfigurationForm; import ee.carlrobert.codegpt.ui.UIUtil; import ee.carlrobert.llm.client.openai.completion.OpenAIChatCompletionModel; import javax.swing.JPanel; +import javax.swing.SwingUtilities; import org.jetbrains.annotations.Nullable; public class OpenAISettingsForm { @@ -28,7 +31,10 @@ public class OpenAISettingsForm { public OpenAISettingsForm(OpenAISettingsState settings) { apiKeyField = new JBPasswordField(); apiKeyField.setColumns(30); - apiKeyField.setText(CredentialsStore.getCredential(OPENAI_API_KEY)); + ApplicationManager.getApplication().executeOnPooledThread(() -> { + var apiKey = CredentialsStore.getCredential(CredentialKey.OPENAI_API_KEY); + SwingUtilities.invokeLater(() -> apiKeyField.setText(apiKey)); + }); organizationField = new JBTextField(settings.getOrganization(), 30); completionModelComboBox = new ComboBox<>( new EnumComboBoxModel<>(OpenAIChatCompletionModel.class)); diff --git a/src/main/java/ee/carlrobert/codegpt/settings/service/you/YouSettingsForm.java b/src/main/java/ee/carlrobert/codegpt/settings/service/you/YouSettingsForm.java index a834b331..e6936bbb 100644 --- a/src/main/java/ee/carlrobert/codegpt/settings/service/you/YouSettingsForm.java +++ b/src/main/java/ee/carlrobert/codegpt/settings/service/you/YouSettingsForm.java @@ -1,9 +1,9 @@ package ee.carlrobert.codegpt.settings.service.you; -import static ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.YOU_ACCOUNT_PASSWORD; import static ee.carlrobert.codegpt.ui.UIUtil.withEmptyLeftBorder; import com.intellij.openapi.Disposable; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.ui.ComponentValidator; import com.intellij.openapi.ui.ValidationInfo; import com.intellij.openapi.util.text.StringUtil; @@ -53,7 +53,10 @@ public class YouSettingsForm extends JPanel { passwordField = new JBPasswordField(); passwordField.setColumns(25); if (!settings.getEmail().isEmpty()) { - passwordField.setText(CredentialsStore.getCredential(YOU_ACCOUNT_PASSWORD)); + ApplicationManager.getApplication().executeOnPooledThread(() -> { + var apiKey = CredentialsStore.getCredential(CredentialKey.YOU_ACCOUNT_PASSWORD); + SwingUtilities.invokeLater(() -> passwordField.setText(apiKey)); + }); } signInButton = new JButton(CodeGPTBundle.get("settingsConfigurable.service.you.signIn.label")); signUpTextPane = createSignUpTextPane(); diff --git a/src/main/kotlin/ee/carlrobert/codegpt/credentials/CredentialsStore.kt b/src/main/kotlin/ee/carlrobert/codegpt/credentials/CredentialsStore.kt index d65953da..3f8433d8 100644 --- a/src/main/kotlin/ee/carlrobert/codegpt/credentials/CredentialsStore.kt +++ b/src/main/kotlin/ee/carlrobert/codegpt/credentials/CredentialsStore.kt @@ -3,16 +3,18 @@ package ee.carlrobert.codegpt.credentials import com.intellij.credentialStore.CredentialAttributes import com.intellij.credentialStore.generateServiceName import com.intellij.ide.passwordSafe.PasswordSafe +import com.intellij.util.concurrency.annotations.RequiresBackgroundThread object CredentialsStore { private val credentialsMap = mutableMapOf() @JvmStatic + @RequiresBackgroundThread fun getCredential(key: CredentialKey): String? = credentialsMap.getOrPut(key) { PasswordSafe.instance.getPassword( CredentialAttributes(generateServiceName("CodeGPT", key.name)) - ) + ) ?: "" } fun setCredential(key: CredentialKey, password: String?) { diff --git a/src/main/kotlin/ee/carlrobert/codegpt/settings/service/codegpt/CodeGPTServiceForm.kt b/src/main/kotlin/ee/carlrobert/codegpt/settings/service/codegpt/CodeGPTServiceForm.kt index 0e6d7704..2bb4870d 100644 --- a/src/main/kotlin/ee/carlrobert/codegpt/settings/service/codegpt/CodeGPTServiceForm.kt +++ b/src/main/kotlin/ee/carlrobert/codegpt/settings/service/codegpt/CodeGPTServiceForm.kt @@ -9,7 +9,6 @@ import com.intellij.ui.components.JBPasswordField import com.intellij.ui.components.fields.IntegerField import com.intellij.util.ui.FormBuilder import ee.carlrobert.codegpt.CodeGPTBundle -import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.CODEGPT_API_KEY import ee.carlrobert.codegpt.credentials.CredentialsStore.getCredential import ee.carlrobert.codegpt.credentials.CredentialsStore.setCredential @@ -18,6 +17,8 @@ import ee.carlrobert.llm.client.codegpt.CodeGPTAvailableModels import ee.carlrobert.llm.client.codegpt.CodeGPTAvailableModels.AVAILABLE_CHAT_MODELS import ee.carlrobert.llm.client.codegpt.CodeGPTAvailableModels.AVAILABLE_CODE_MODELS import ee.carlrobert.llm.client.codegpt.CodeGPTModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking import org.jdesktop.swingx.combobox.ListComboBoxModel import java.awt.Component import javax.swing.DefaultListCellRenderer @@ -28,7 +29,6 @@ class CodeGPTServiceForm { private val apiKeyField = JBPasswordField().apply { columns = 30 - text = getCredential(CredentialKey.CUSTOM_SERVICE_API_KEY) } private val chatCompletionModelComboBox = @@ -56,6 +56,12 @@ class CodeGPTServiceForm { value = service().state.codeCompletionSettings.maxTokens } + init { + apiKeyField.text = runBlocking(Dispatchers.IO) { + getCredential(CODEGPT_API_KEY) + } + } + fun getForm(): JPanel = FormBuilder.createFormBuilder() .addComponent(TitledSeparator(CodeGPTBundle.get("shared.configuration"))) .addComponent( diff --git a/src/main/kotlin/ee/carlrobert/codegpt/settings/service/custom/form/CustomServiceForm.kt b/src/main/kotlin/ee/carlrobert/codegpt/settings/service/custom/form/CustomServiceForm.kt index 83085c3e..c56d4618 100644 --- a/src/main/kotlin/ee/carlrobert/codegpt/settings/service/custom/form/CustomServiceForm.kt +++ b/src/main/kotlin/ee/carlrobert/codegpt/settings/service/custom/form/CustomServiceForm.kt @@ -16,6 +16,8 @@ import ee.carlrobert.codegpt.settings.service.custom.CustomServiceCodeCompletion import ee.carlrobert.codegpt.settings.service.custom.CustomServiceSettings import ee.carlrobert.codegpt.settings.service.custom.template.CustomServiceTemplate import ee.carlrobert.codegpt.ui.UIUtil +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking import java.awt.FlowLayout import java.net.MalformedURLException import java.net.URL @@ -27,7 +29,6 @@ class CustomServiceForm { private val apiKeyField = JBPasswordField().apply { columns = 30 - text = getCredential(CredentialKey.CUSTOM_SERVICE_API_KEY) } private val templateHelpText = JBLabel(General.ContextHelp) private val templateComboBox = ComboBox(EnumComboBoxModel(CustomServiceTemplate::class.java)) @@ -37,6 +38,9 @@ class CustomServiceForm { init { val state = service().state + apiKeyField.text = runBlocking(Dispatchers.IO) { + getCredential(CredentialKey.CUSTOM_SERVICE_API_KEY) + } chatCompletionsForm = CustomServiceChatCompletionForm(state.chatCompletionSettings, this::getApiKey) codeCompletionsForm = diff --git a/src/main/kotlin/ee/carlrobert/codegpt/settings/service/google/GoogleSettingsForm.kt b/src/main/kotlin/ee/carlrobert/codegpt/settings/service/google/GoogleSettingsForm.kt index 060783b6..a04a26d9 100644 --- a/src/main/kotlin/ee/carlrobert/codegpt/settings/service/google/GoogleSettingsForm.kt +++ b/src/main/kotlin/ee/carlrobert/codegpt/settings/service/google/GoogleSettingsForm.kt @@ -8,10 +8,12 @@ import com.intellij.ui.components.JBPasswordField import com.intellij.util.ui.FormBuilder import com.intellij.util.ui.UI import ee.carlrobert.codegpt.CodeGPTBundle -import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey +import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.GOOGLE_API_KEY import ee.carlrobert.codegpt.credentials.CredentialsStore.getCredential import ee.carlrobert.codegpt.ui.UIUtil import ee.carlrobert.llm.client.google.models.GoogleModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking import javax.swing.JPanel import javax.swing.event.HyperlinkEvent @@ -23,8 +25,9 @@ class GoogleSettingsForm { init { val state = service().state apiKeyField.columns = 30 - apiKeyField.text = - getCredential(CredentialKey.GOOGLE_API_KEY) + apiKeyField.text = runBlocking(Dispatchers.IO) { + getCredential(GOOGLE_API_KEY) + } completionModelComboBox = ComboBox( EnumComboBoxModel(GoogleModel::class.java) ) @@ -75,13 +78,12 @@ class GoogleSettingsForm { fun resetForm() { val state = service().state - apiKeyField.text = - getCredential(CredentialKey.GOOGLE_API_KEY) + apiKeyField.text = getCredential(GOOGLE_API_KEY) completionModelComboBox.selectedItem = GoogleModel.findByCode(state.model) } fun isModified(): Boolean = service().state.run { - model != getModel() || getApiKey() != getCredential(CredentialKey.GOOGLE_API_KEY) + model != getModel() || getApiKey() != getCredential(GOOGLE_API_KEY) } fun applyChanges() { @@ -89,5 +91,4 @@ class GoogleSettingsForm { model = getModel() } } - }