From 755791aea88fd6d7ce1a22da8c6225ff06deaf7c Mon Sep 17 00:00:00 2001 From: Carl-Robert Linnupuu Date: Thu, 14 Nov 2024 13:56:07 +0000 Subject: [PATCH] refactor: rewrite method lookup listener and improve error handling --- .../completions/MethodNameLookupListener.java | 102 ----------------- .../MethodNameCompletionLookupListener.kt | 103 ++++++++++++++++++ src/main/resources/META-INF/plugin.xml | 2 +- 3 files changed, 104 insertions(+), 103 deletions(-) delete mode 100644 src/main/java/ee/carlrobert/codegpt/completions/MethodNameLookupListener.java create mode 100644 src/main/kotlin/ee/carlrobert/codegpt/completions/MethodNameCompletionLookupListener.kt diff --git a/src/main/java/ee/carlrobert/codegpt/completions/MethodNameLookupListener.java b/src/main/java/ee/carlrobert/codegpt/completions/MethodNameLookupListener.java deleted file mode 100644 index 18200139..00000000 --- a/src/main/java/ee/carlrobert/codegpt/completions/MethodNameLookupListener.java +++ /dev/null @@ -1,102 +0,0 @@ -package ee.carlrobert.codegpt.completions; - -import static ee.carlrobert.codegpt.CodeGPTKeys.IS_PROMPT_TEXT_FIELD_DOCUMENT; - -import com.intellij.codeInsight.completion.PrefixMatcher; -import com.intellij.codeInsight.lookup.Lookup; -import com.intellij.codeInsight.lookup.LookupElementBuilder; -import com.intellij.codeInsight.lookup.LookupManagerListener; -import com.intellij.codeInsight.lookup.impl.LookupImpl; -import com.intellij.openapi.application.Application; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.psi.PsiElement; -import com.intellij.psi.tree.IElementType; -import com.intellij.psi.util.PsiUtilCore; -import ee.carlrobert.codegpt.Icons; -import ee.carlrobert.codegpt.settings.configuration.ConfigurationSettings; -import java.util.List; -import java.util.Optional; -import org.jetbrains.annotations.Nullable; - -public class MethodNameLookupListener implements LookupManagerListener { - - private static final Logger LOG = Logger.getInstance(MethodNameLookupListener.class); - - @Override - public void activeLookupChanged(@Nullable Lookup oldLookup, @Nullable Lookup newLookup) { - if (!(newLookup instanceof LookupImpl lookup)) { - return; - } - - Boolean isPromptTextFieldDocument = IS_PROMPT_TEXT_FIELD_DOCUMENT.get( - lookup.getEditor().getDocument()); - if ((isPromptTextFieldDocument != null && isPromptTextFieldDocument) - || !ConfigurationSettings.getState().getMethodNameGenerationEnabled() - || !CompletionRequestService.isRequestAllowed()) { - return; - } - - var application = ApplicationManager.getApplication(); - Optional.ofNullable(lookup.getPsiElement()) - .map(PsiElement::getContext) - .ifPresent(context -> - application.runReadAction(() -> { - var type = PsiUtilCore.getElementType(context); - if (PSIMethodMapping.contains(type)) { - var selection = context.getText(); - application.executeOnPooledThread(() -> - addCompletionLookupValues(lookup, application, selection)); - } - })); - } - - private void addCompletionLookupValues( - LookupImpl lookup, - Application application, - String prompt) { - try { - var response = CompletionRequestService.getInstance() - .getLookupCompletion(new LookupCompletionParameters(prompt)); - if (!response.isEmpty()) { - for (var value : response.split(",")) { - application.invokeLater(() -> application.runReadAction(() -> { - lookup.addItem( - LookupElementBuilder.create(value.trim()).withIcon(Icons.Sparkle), - PrefixMatcher.ALWAYS_TRUE); - lookup.refreshUi(true, true); - })); - } - } - } catch (Exception e) { - throw new RuntimeException("Failed to add completion lookup values", e); - } - } - - private enum PSIMethodMapping { - GO(List.of("FILE", "METHOD_DECLARATION|FUNCTION_DECLARATION")), - JAVA(List.of("java.FILE", "METHOD")), - PY(List.of("FILE", "Py:FUNCTION_DECLARATION")), - JAVASCRIPT(List.of("JS:FUNCTION_DECLARATION", "JS:TYPESCRIPT_FUNCTION")), - CS(List.of("FILE", "DUMMY_TYPE_DECLARATION", "DUMMY_BLOCK")), - PHP(List.of("FILE", "Class method|function|Function")), - KOTLIN(List.of("FUN")), - DEFAULT(List.of("FILE", "METHOD_DECLARATION")); - - private final List types; - - PSIMethodMapping(List codes) { - this.types = codes; - } - - public static boolean contains(IElementType type) { - LOG.info("Trying to find method mapping for type: " + type.toString()); - for (var value : PSIMethodMapping.values()) { - if (value.types.contains(type.toString())) { - return true; - } - } - return false; - } - } -} diff --git a/src/main/kotlin/ee/carlrobert/codegpt/completions/MethodNameCompletionLookupListener.kt b/src/main/kotlin/ee/carlrobert/codegpt/completions/MethodNameCompletionLookupListener.kt new file mode 100644 index 00000000..eee6b7e7 --- /dev/null +++ b/src/main/kotlin/ee/carlrobert/codegpt/completions/MethodNameCompletionLookupListener.kt @@ -0,0 +1,103 @@ +package ee.carlrobert.codegpt.completions + +import com.intellij.codeInsight.completion.PrefixMatcher +import com.intellij.codeInsight.lookup.Lookup +import com.intellij.codeInsight.lookup.LookupElementBuilder +import com.intellij.codeInsight.lookup.LookupManagerListener +import com.intellij.codeInsight.lookup.impl.LookupImpl +import com.intellij.notification.NotificationType +import com.intellij.openapi.application.runInEdt +import com.intellij.openapi.application.runReadAction +import com.intellij.openapi.components.service +import com.intellij.openapi.diagnostic.thisLogger +import com.intellij.psi.tree.IElementType +import com.intellij.psi.util.PsiUtilCore +import ee.carlrobert.codegpt.CodeGPTKeys.IS_PROMPT_TEXT_FIELD_DOCUMENT +import ee.carlrobert.codegpt.Icons +import ee.carlrobert.codegpt.settings.configuration.ConfigurationSettings +import ee.carlrobert.codegpt.ui.OverlayUtil +import ee.carlrobert.llm.client.codegpt.response.CodeGPTException +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.launch + +class MethodNameCompletionLookupListener : LookupManagerListener { + + companion object { + private val logger = thisLogger() + } + + private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default) + + override fun activeLookupChanged(oldLookup: Lookup?, newLookup: Lookup?) { + if (newLookup !is LookupImpl) { + return + } + + val isPromptTextFieldDocument = + IS_PROMPT_TEXT_FIELD_DOCUMENT[newLookup.editor.document] ?: false + val featureEnabled = service().state.methodNameGenerationEnabled + if (isPromptTextFieldDocument || !featureEnabled || !CompletionRequestService.isRequestAllowed()) { + return + } + + newLookup.psiElement?.context?.let { context -> + val elementType = PsiUtilCore.getElementType(context) + if (PSIMethodMapping.contains(elementType)) { + val selection = runReadAction { context.text } + scope.launch { + try { + val lookupCompletion = service() + .getLookupCompletion(LookupCompletionParameters(selection)) + if (lookupCompletion.isNotEmpty()) { + addCompletionLookupValues(newLookup, lookupCompletion) + } + } catch (ex: CodeGPTException) { + OverlayUtil.showNotification(ex.detail, NotificationType.ERROR) + } catch (ex: Exception) { + logger.error( + "Something went wrong while requesting completion lookup values.", + ex + ) + } + } + } + } + } + + private fun addCompletionLookupValues(lookup: LookupImpl, response: String) { + val values = response.split(",".toRegex()) + .dropLastWhile { it.isEmpty() } + .toTypedArray() + for (value in values) { + runInEdt { + lookup.addItem( + LookupElementBuilder + .create(value.trim { it <= ' ' }) + .withIcon(Icons.DefaultSmall), + PrefixMatcher.ALWAYS_TRUE + ) + lookup.refreshUi(true, true) + } + } + } + + private enum class PSIMethodMapping(val types: List) { + GO(listOf("FILE", "METHOD_DECLARATION|FUNCTION_DECLARATION")), + JAVA(listOf("java.FILE", "METHOD")), + PY(listOf("FILE", "Py:FUNCTION_DECLARATION")), + JAVASCRIPT(listOf("JS:FUNCTION_DECLARATION", "JS:TYPESCRIPT_FUNCTION")), + CS(listOf("FILE", "DUMMY_TYPE_DECLARATION", "DUMMY_BLOCK")), + PHP(listOf("FILE", "Class method|function|Function")), + KOTLIN(listOf("FUN")), + DEFAULT(listOf("FILE", "METHOD_DECLARATION")); + + + companion object { + fun contains(type: IElementType): Boolean { + return entries.any { it.types.contains(type.toString()) } + } + } + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 0d859663..360fce94 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -16,7 +16,7 @@ + class="ee.carlrobert.codegpt.completions.MethodNameCompletionLookupListener"/>