From e3fdf9859018664bf8bce74868b01914b7c2fe37 Mon Sep 17 00:00:00 2001 From: Kirill Starkov Date: Wed, 11 Dec 2024 17:15:50 +0800 Subject: [PATCH 1/9] disable logs for jcef --- .../com/smallcloud/refactai/panes/sharedchat/SharedChatPane.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/SharedChatPane.kt b/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/SharedChatPane.kt index 5b648fc0..5a796dcf 100644 --- a/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/SharedChatPane.kt +++ b/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/SharedChatPane.kt @@ -509,7 +509,7 @@ class SharedChatPane(val project: Project) : JPanel(), Disposable { } val webView by lazy { - System.setProperty("ide.browser.jcef.log.level", "info") +// System.setProperty("ide.browser.jcef.log.level", "info") browser.webView } From 854e990da97249198c89219c27e5c6b68076bd80 Mon Sep 17 00:00:00 2001 From: Kirill Starkov Date: Wed, 11 Dec 2024 17:29:19 +0800 Subject: [PATCH 2/9] fix robot telemetry --- .../refactai/codecompletion/RefactAICompletionProvider.kt | 5 ++++- .../com/smallcloud/refactai/listeners/AcceptAction.kt | 7 +++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/smallcloud/refactai/codecompletion/RefactAICompletionProvider.kt b/src/main/kotlin/com/smallcloud/refactai/codecompletion/RefactAICompletionProvider.kt index c9791cdb..79169966 100644 --- a/src/main/kotlin/com/smallcloud/refactai/codecompletion/RefactAICompletionProvider.kt +++ b/src/main/kotlin/com/smallcloud/refactai/codecompletion/RefactAICompletionProvider.kt @@ -18,6 +18,7 @@ import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.editor.Document import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Key import com.intellij.ui.components.JBLabel import com.intellij.util.application import com.intellij.util.ui.JBFont @@ -50,6 +51,8 @@ import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds import com.smallcloud.refactai.io.InferenceGlobalContext.Companion.instance as InferenceGlobalContext +val EditorRefactLastSnippetTelemetryIdKey = Key.create("refact.snippetTelemetryId") + private class Default : InlineCompletionSuggestionUpdateManager.Adapter { override fun onDocumentChange( event: InlineCompletionEvent.DocumentChange, @@ -253,10 +256,10 @@ class RefactAICompletionProvider : DebouncedInlineCompletionProvider() { send(it) delay(2) } + EditorRefactLastSnippetTelemetryIdKey[request.editor] = completion.snippetTelemetryId } } awaitClose() - }) private fun getSingleLineElements( diff --git a/src/main/kotlin/com/smallcloud/refactai/listeners/AcceptAction.kt b/src/main/kotlin/com/smallcloud/refactai/listeners/AcceptAction.kt index 7956f5a3..ff03f200 100644 --- a/src/main/kotlin/com/smallcloud/refactai/listeners/AcceptAction.kt +++ b/src/main/kotlin/com/smallcloud/refactai/listeners/AcceptAction.kt @@ -5,14 +5,17 @@ import com.intellij.codeInsight.hint.HintManagerImpl.ActionToIgnore import com.intellij.codeInsight.inline.completion.InlineCompletion import com.intellij.codeInsight.inline.completion.session.InlineCompletionContext import com.intellij.openapi.actionSystem.DataContext +import com.intellij.openapi.components.service import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.editor.Caret import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.actionSystem.EditorAction import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler import com.smallcloud.refactai.Resources +import com.smallcloud.refactai.codecompletion.EditorRefactLastSnippetTelemetryIdKey import com.smallcloud.refactai.codecompletion.InlineCompletionGrayTextElementCustom import com.smallcloud.refactai.modes.ModeProvider +import com.smallcloud.refactai.statistic.UsageStats const val ACTION_ID_ = "TabPressedAction" @@ -29,6 +32,10 @@ class TabPressedAction : EditorAction(InlineCompletionHandler()), ActionToIgnore val provider = ModeProvider.getOrCreateModeProvider(editor) if (provider.isInCompletionMode()) { InlineCompletion.getHandlerOrNull(editor)?.insert() + EditorRefactLastSnippetTelemetryIdKey[editor]?.also { + editor.project?.service()?.snippetAccepted(it) + EditorRefactLastSnippetTelemetryIdKey[editor] = null + } } else { provider.onTabPressed(editor, caret, dataContext) } From fc1a3c81cbe4a78a9e05181e238c15d23c3ee2ac Mon Sep 17 00:00:00 2001 From: Kirill Starkov Date: Wed, 11 Dec 2024 19:09:25 +0800 Subject: [PATCH 3/9] add open chat event telemetry --- .../com/smallcloud/refactai/Resources.kt | 1 + .../refactai/code_lens/CodeLensAction.kt | 4 ++ .../panes/sharedchat/ChatPaneInvokeAction.kt | 6 ++- .../refactai/statistic/UsageStats.kt | 43 +++++++++++++++++++ 4 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/smallcloud/refactai/Resources.kt b/src/main/kotlin/com/smallcloud/refactai/Resources.kt index bc23e020..e71a7cda 100644 --- a/src/main/kotlin/com/smallcloud/refactai/Resources.kt +++ b/src/main/kotlin/com/smallcloud/refactai/Resources.kt @@ -65,6 +65,7 @@ object Resources { val defaultCodeCompletionUrlSuffix = URI("v1/code-completion") val cloudUserMessage: URI = defaultCloudUrl.resolve("/v1/user-message") val defaultReportUrlSuffix: URI = URI("v1/telemetry-network") + val defaultChatReportUrlSuffix: URI = URI("v1/telemetry-chat") val defaultSnippetAcceptedUrlSuffix: URI = URI("v1/snippet-accepted") val version: String = getVersion() const val client: String = "jetbrains" diff --git a/src/main/kotlin/com/smallcloud/refactai/code_lens/CodeLensAction.kt b/src/main/kotlin/com/smallcloud/refactai/code_lens/CodeLensAction.kt index 52c58d0a..a4fde2ba 100644 --- a/src/main/kotlin/com/smallcloud/refactai/code_lens/CodeLensAction.kt +++ b/src/main/kotlin/com/smallcloud/refactai/code_lens/CodeLensAction.kt @@ -2,6 +2,7 @@ package com.smallcloud.refactai.code_lens import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.components.service import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.LogicalPosition import com.intellij.openapi.project.DumbAwareAction @@ -9,6 +10,8 @@ import com.intellij.openapi.roots.ProjectRootManager import com.intellij.openapi.wm.ToolWindowManager import com.smallcloud.refactai.Resources import com.smallcloud.refactai.panes.RefactAIToolboxPaneFactory +import com.smallcloud.refactai.statistic.UsageStatistic +import com.smallcloud.refactai.statistic.UsageStats import java.util.concurrent.atomic.AtomicBoolean import kotlin.io.path.relativeTo @@ -50,6 +53,7 @@ class CodeLensAction( chat?.activate { RefactAIToolboxPaneFactory.chat?.requestFocus() RefactAIToolboxPaneFactory.chat?.executeCodeLensCommand(formatMessage(), sendImmediately, openNewTab) + editor.project?.service()?.addChatStatistic(true, UsageStatistic("openChatByCodelens"), "") } // If content is empty, then it's "Open Chat" instruction, selecting range of code in active tab diff --git a/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/ChatPaneInvokeAction.kt b/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/ChatPaneInvokeAction.kt index a9799221..860dac4b 100644 --- a/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/ChatPaneInvokeAction.kt +++ b/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/ChatPaneInvokeAction.kt @@ -2,9 +2,12 @@ package com.smallcloud.refactai.panes.sharedchat import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.components.service import com.intellij.openapi.wm.ToolWindowManager import com.smallcloud.refactai.Resources import com.smallcloud.refactai.panes.RefactAIToolboxPaneFactory +import com.smallcloud.refactai.statistic.UsageStatistic +import com.smallcloud.refactai.statistic.UsageStats import com.smallcloud.refactai.utils.getLastUsedProject class ChatPaneInvokeAction: AnAction(Resources.Icons.LOGO_RED_16x16) { @@ -14,8 +17,9 @@ class ChatPaneInvokeAction: AnAction(Resources.Icons.LOGO_RED_16x16) { fun actionPerformed() { val chat = ToolWindowManager.getInstance(getLastUsedProject()).getToolWindow("Refact") - chat?.activate{ + chat?.activate { RefactAIToolboxPaneFactory.focusChat() + getLastUsedProject().service().addChatStatistic(true, UsageStatistic("openChatByShortcut"), "") } } } \ No newline at end of file diff --git a/src/main/kotlin/com/smallcloud/refactai/statistic/UsageStats.kt b/src/main/kotlin/com/smallcloud/refactai/statistic/UsageStats.kt index 6873c5fe..acb91422 100644 --- a/src/main/kotlin/com/smallcloud/refactai/statistic/UsageStats.kt +++ b/src/main/kotlin/com/smallcloud/refactai/statistic/UsageStats.kt @@ -7,6 +7,7 @@ import com.intellij.openapi.components.service import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.project.Project import com.intellij.util.concurrency.AppExecutorUtil +import com.smallcloud.refactai.Resources.defaultChatReportUrlSuffix import com.smallcloud.refactai.Resources.defaultReportUrlSuffix import com.smallcloud.refactai.Resources.defaultSnippetAcceptedUrlSuffix import com.smallcloud.refactai.io.sendRequest @@ -93,6 +94,48 @@ class UsageStats(private val project: Project): Disposable { } } } + + fun addChatStatistic( + positive: Boolean, + stat: UsageStatistic, + errorMessage: Any + ) { + var errorMessageStr = errorMessage.toString() + val gson = Gson() + if (errorMessageStr.length > 200) { + errorMessageStr = errorMessageStr.substring(0, 200) + "…" + } + + val errorMessageJson = gson.toJson(errorMessageStr) + var scope = stat.scope + if (stat.subScope.isNotEmpty()) { + scope += ":" + stat.subScope + } + + val scopeJson = gson.toJson(scope) + val body = gson.toJson( + mapOf( + "success" to positive, + "error_message" to errorMessageJson, + "scope" to scopeJson, + ) + ) + val url = getLSPProcessHolder(project)!!.url.resolve(defaultChatReportUrlSuffix) + execService.submit { + try { + val res = sendRequest(url, "POST", body=body) + if (res.body.isNullOrEmpty()) return@submit + + val json = gson.fromJson(res.body, JsonObject::class.java) + val success = if (json.has("success")) json.get("success").asInt else null + if (success != null && success != 1) { + throw Exception(json.get("human_readable_message").asString) + } + } catch (e: Exception) { + Logger.getInstance(UsageStats::class.java).warn("report to $url failed: $e") + } + } + } companion object { @JvmStatic From 9932ac69f3444916f482af98ba18ff089f10e533 Mon Sep 17 00:00:00 2001 From: Kirill Starkov Date: Fri, 13 Dec 2024 21:58:52 +0800 Subject: [PATCH 4/9] TMP CICD CHANGES --- .github/workflows/build.yml | 2 +- refact_lsp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f4f131f2..c6226702 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,7 +43,7 @@ jobs: workflow: node.js.yml workflow_search: true repo: smallcloudai/refact-chat-js - branch: main + branch: alpha name: refact-chat-js-latest path: ./src/main/resources/webview/dist diff --git a/refact_lsp b/refact_lsp index a6eeb03f..312e7df8 100644 --- a/refact_lsp +++ b/refact_lsp @@ -1 +1 @@ -v0.10.5 \ No newline at end of file +self_configure \ No newline at end of file From 5577ad7d5108c2f0b41e0afb90516fc147ab70d5 Mon Sep 17 00:00:00 2001 From: Kirill Starkov Date: Fri, 20 Dec 2024 00:19:39 +0800 Subject: [PATCH 5/9] fix completion action on win --- .../com/smallcloud/refactai/Initializer.kt | 38 ------------------- .../RefactAICompletionProvider.kt | 2 + .../refactai/listeners/AcceptAction.kt | 12 ++++-- .../listeners/AcceptActionPromoter.kt | 12 ++++-- .../GenerateGitCommitMessageAction.kt | 4 +- src/main/resources/META-INF/plugin.xml | 16 ++++---- 6 files changed, 28 insertions(+), 56 deletions(-) diff --git a/src/main/kotlin/com/smallcloud/refactai/Initializer.kt b/src/main/kotlin/com/smallcloud/refactai/Initializer.kt index 64620cee..ce32be75 100644 --- a/src/main/kotlin/com/smallcloud/refactai/Initializer.kt +++ b/src/main/kotlin/com/smallcloud/refactai/Initializer.kt @@ -40,44 +40,6 @@ class Initializer : ProjectActivity, Disposable { PluginInstaller.addStateListener(UninstallListener()) UpdateChecker.instance - ApplicationManager.getApplication() - .messageBus - .connect(PluginState.instance) - .subscribe(KeymapManagerListener.TOPIC, object : KeymapManagerListener { - override fun shortcutsChanged( - keymap: Keymap, - actionIds: MutableCollection, - fromSettings: Boolean - ) { - if (Thread.currentThread().stackTrace.count { it.className.startsWith("com.smallcloud.refactai.Initializer") } > 1) { - return - } - for (id in actionIds) { - if (!listOf(IdeActions.ACTION_INSERT_INLINE_COMPLETION, ACTION_ID_).contains(id)) { - continue - } - val shortcuts = keymap.getShortcuts(id) - if (id == IdeActions.ACTION_INSERT_INLINE_COMPLETION) { - keymap.removeAllActionShortcuts(ACTION_ID_) - for (shortcut in shortcuts) { - keymap.addShortcut( - ACTION_ID_, - shortcut - ) - } - } else if (id == ACTION_ID_) { - keymap.removeAllActionShortcuts(IdeActions.ACTION_INSERT_INLINE_COMPLETION) - for (shortcut in shortcuts) { - keymap.addShortcut( - IdeActions.ACTION_INSERT_INLINE_COMPLETION, - shortcut - ) - } - } - } - } - }) - ApplicationManager.getApplication().getService(CloudMessageService::class.java) if (!isJcefCanStart()) { emitInfo(RefactAIBundle.message("notifications.chatCanNotStartWarning"), false) diff --git a/src/main/kotlin/com/smallcloud/refactai/codecompletion/RefactAICompletionProvider.kt b/src/main/kotlin/com/smallcloud/refactai/codecompletion/RefactAICompletionProvider.kt index 79169966..ae5e3605 100644 --- a/src/main/kotlin/com/smallcloud/refactai/codecompletion/RefactAICompletionProvider.kt +++ b/src/main/kotlin/com/smallcloud/refactai/codecompletion/RefactAICompletionProvider.kt @@ -52,6 +52,7 @@ import kotlin.time.Duration.Companion.milliseconds import com.smallcloud.refactai.io.InferenceGlobalContext.Companion.instance as InferenceGlobalContext val EditorRefactLastSnippetTelemetryIdKey = Key.create("refact.snippetTelemetryId") +val EditorRefactLastCompletionIsMultilineKey = Key.create("refact.lastCompletion.isMultiline") private class Default : InlineCompletionSuggestionUpdateManager.Adapter { override fun onDocumentChange( @@ -257,6 +258,7 @@ class RefactAICompletionProvider : DebouncedInlineCompletionProvider() { delay(2) } EditorRefactLastSnippetTelemetryIdKey[request.editor] = completion.snippetTelemetryId + EditorRefactLastCompletionIsMultilineKey[request.editor] = completion.multiline } } awaitClose() diff --git a/src/main/kotlin/com/smallcloud/refactai/listeners/AcceptAction.kt b/src/main/kotlin/com/smallcloud/refactai/listeners/AcceptAction.kt index ff03f200..fbcaed7d 100644 --- a/src/main/kotlin/com/smallcloud/refactai/listeners/AcceptAction.kt +++ b/src/main/kotlin/com/smallcloud/refactai/listeners/AcceptAction.kt @@ -12,6 +12,7 @@ import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.actionSystem.EditorAction import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler import com.smallcloud.refactai.Resources +import com.smallcloud.refactai.codecompletion.EditorRefactLastCompletionIsMultilineKey import com.smallcloud.refactai.codecompletion.EditorRefactLastSnippetTelemetryIdKey import com.smallcloud.refactai.codecompletion.InlineCompletionGrayTextElementCustom import com.smallcloud.refactai.modes.ModeProvider @@ -19,14 +20,14 @@ import com.smallcloud.refactai.statistic.UsageStats const val ACTION_ID_ = "TabPressedAction" -class TabPressedAction : EditorAction(InlineCompletionHandler()), ActionToIgnore { +class TabPressedAction : EditorAction(InsertInlineCompletionHandler()), ActionToIgnore { val ACTION_ID = ACTION_ID_ init { this.templatePresentation.icon = Resources.Icons.LOGO_RED_16x16 } - class InlineCompletionHandler : EditorWriteActionHandler() { + class InsertInlineCompletionHandler : EditorWriteActionHandler() { override fun executeWriteAction(editor: Editor, caret: Caret?, dataContext: DataContext) { Logger.getInstance("RefactTabPressedAction").debug("executeWriteAction") val provider = ModeProvider.getOrCreateModeProvider(editor) @@ -35,6 +36,7 @@ class TabPressedAction : EditorAction(InlineCompletionHandler()), ActionToIgnore EditorRefactLastSnippetTelemetryIdKey[editor]?.also { editor.project?.service()?.snippetAccepted(it) EditorRefactLastSnippetTelemetryIdKey[editor] = null + EditorRefactLastCompletionIsMultilineKey[editor] = null } } else { provider.onTabPressed(editor, caret, dataContext) @@ -51,8 +53,10 @@ class TabPressedAction : EditorAction(InlineCompletionHandler()), ActionToIgnore val ctx = InlineCompletionContext.getOrNull(editor) ?: return false if (ctx.state.elements.size != 1) return false val elem = ctx.state.elements.first() - if (elem !is InlineCompletionGrayTextElementCustom.Presentable) return false - return elem.delta == caret.logicalPosition.column + val isMultiline = EditorRefactLastCompletionIsMultilineKey[editor] + if (isMultiline && elem is InlineCompletionGrayTextElementCustom.Presentable) + return elem.delta == caret.logicalPosition.column + return true } else { return ModeProvider.getOrCreateModeProvider(editor).modeInActiveState() } diff --git a/src/main/kotlin/com/smallcloud/refactai/listeners/AcceptActionPromoter.kt b/src/main/kotlin/com/smallcloud/refactai/listeners/AcceptActionPromoter.kt index f525988d..ffa46641 100644 --- a/src/main/kotlin/com/smallcloud/refactai/listeners/AcceptActionPromoter.kt +++ b/src/main/kotlin/com/smallcloud/refactai/listeners/AcceptActionPromoter.kt @@ -1,5 +1,6 @@ package com.smallcloud.refactai.listeners +import com.intellij.codeInsight.inline.completion.session.InlineCompletionContext import com.intellij.openapi.actionSystem.ActionPromoter import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.CommonDataKeys @@ -10,9 +11,12 @@ class AcceptActionsPromoter : ActionPromoter { private fun getEditor(dataContext: DataContext): Editor? { return CommonDataKeys.EDITOR.getData(dataContext) } - override fun promote(actions: MutableList, context: DataContext): MutableList { - if (getEditor(context) == null) - return actions.toMutableList() - return actions.filterIsInstance().toMutableList() + override fun promote(actions: MutableList, context: DataContext): List { + val editor = getEditor(context) ?: return emptyList() + if (InlineCompletionContext.getOrNull(editor) == null) { + return emptyList() + } + actions.filterIsInstance().takeIf { it.isNotEmpty() }?.let { return it } + return emptyList() } } \ No newline at end of file diff --git a/src/main/kotlin/com/smallcloud/refactai/listeners/GenerateGitCommitMessageAction.kt b/src/main/kotlin/com/smallcloud/refactai/listeners/GenerateGitCommitMessageAction.kt index 33e51cdb..140ea980 100644 --- a/src/main/kotlin/com/smallcloud/refactai/listeners/GenerateGitCommitMessageAction.kt +++ b/src/main/kotlin/com/smallcloud/refactai/listeners/GenerateGitCommitMessageAction.kt @@ -37,7 +37,7 @@ class GenerateGitCommitMessageAction : AnAction( val lspService = event.project?.service() ?: return@invokeLater - val isEnabled = lspService.isWorking && (commitWorkflowUi.getIncludedChanges().isNotEmpty() && commitWorkflowUi.getIncludedUnversionedFiles().isNotEmpty()) + val isEnabled = lspService.isWorking && (commitWorkflowUi.getIncludedChanges().isNotEmpty() || commitWorkflowUi.getIncludedUnversionedFiles().isNotEmpty()) event.presentation.isEnabled = isEnabled event.presentation.text = if (lspService.isWorking) { @@ -54,7 +54,6 @@ class GenerateGitCommitMessageAction : AnAction( return } - val gitDiff = getDiff(event, project) ?: return val commitWorkflowUi = event.getData(VcsDataKeys.COMMIT_WORKFLOW_UI) if (commitWorkflowUi != null) { @@ -87,7 +86,6 @@ class GenerateGitCommitMessageAction : AnAction( try { val includedChanges = commitWorkflowUi.getIncludedChanges() - commitWorkflowUi.getIncludedUnversionedFiles() val filePatches = IdeaTextPatchBuilder.buildPatch( project, includedChanges, projectFileVcsRoot.toNioPath(), false, true ) diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 05ef895b..4bb36ad9 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -148,9 +148,11 @@ integrated into a single package that follows your privacy settings.

+ - - - - - - + + + + bundles.RefactAI From f7a19a3750b36d4c51807a8b7a3020a3ff54d88b Mon Sep 17 00:00:00 2001 From: Kirill Starkov Date: Fri, 20 Dec 2024 23:39:22 +0800 Subject: [PATCH 6/9] fix completion filepath --- refact_lsp | 2 +- .../refactai/codecompletion/RefactAICompletionProvider.kt | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/refact_lsp b/refact_lsp index 312e7df8..88d050b1 100644 --- a/refact_lsp +++ b/refact_lsp @@ -1 +1 @@ -self_configure \ No newline at end of file +main \ No newline at end of file diff --git a/src/main/kotlin/com/smallcloud/refactai/codecompletion/RefactAICompletionProvider.kt b/src/main/kotlin/com/smallcloud/refactai/codecompletion/RefactAICompletionProvider.kt index ae5e3605..6117b8c5 100644 --- a/src/main/kotlin/com/smallcloud/refactai/codecompletion/RefactAICompletionProvider.kt +++ b/src/main/kotlin/com/smallcloud/refactai/codecompletion/RefactAICompletionProvider.kt @@ -148,17 +148,16 @@ class RefactAICompletionProvider : DebouncedInlineCompletionProvider() { override fun restartOn(event: InlineCompletionEvent): Boolean = false - private fun getActiveFile(document: Document, project: Project?): String? { - val projectPath = project?.basePath ?: return null + private fun getActiveFile(document: Document): String? { val file = FileDocumentManager.getInstance().getFile(document) ?: return null - return Path(file.path).toUri().toString().replace(Path(projectPath).toUri().toString(), "") + return Path(file.path).toString() } private class Context(val request: SMCRequest, val editorState: EditorTextState, val force: Boolean = false) private fun makeContext(request: InlineCompletionRequest): Context? { - val fileName = getActiveFile(request.document, request.editor.project) ?: return null + val fileName = getActiveFile(request.document) ?: return null if (PrivacyService.instance.getPrivacy(FileDocumentManager.getInstance().getFile(request.document)) == Privacy.DISABLED && !InferenceGlobalContext.isSelfHosted ) return null From 92c6535724f7f964fa309f8b96178294764076f6 Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Wed, 8 Jan 2025 19:07:07 +0100 Subject: [PATCH 7/9] wip: working with messages, functions to process message.content --- .../refactai/code_lens/CodeLensAction.kt | 44 ++++++++++++++++--- .../refactai/panes/sharedchat/ChatPanes.kt | 5 ++- .../refactai/panes/sharedchat/Events.kt | 17 ++++++- .../panes/sharedchat/SharedChatPane.kt | 9 ++-- 4 files changed, 61 insertions(+), 14 deletions(-) diff --git a/src/main/kotlin/com/smallcloud/refactai/code_lens/CodeLensAction.kt b/src/main/kotlin/com/smallcloud/refactai/code_lens/CodeLensAction.kt index a4fde2ba..f54f567c 100644 --- a/src/main/kotlin/com/smallcloud/refactai/code_lens/CodeLensAction.kt +++ b/src/main/kotlin/com/smallcloud/refactai/code_lens/CodeLensAction.kt @@ -12,6 +12,7 @@ import com.smallcloud.refactai.Resources import com.smallcloud.refactai.panes.RefactAIToolboxPaneFactory import com.smallcloud.refactai.statistic.UsageStatistic import com.smallcloud.refactai.statistic.UsageStats +import com.smallcloud.refactai.struct.ChatMessage import java.util.concurrent.atomic.AtomicBoolean import kotlin.io.path.relativeTo @@ -19,7 +20,7 @@ class CodeLensAction( private val editor: Editor, private val line1: Int, private val line2: Int, - private val contentMsg: String, + private val messages: Array, private val sendImmediately: Boolean, private val openNewTab: Boolean ) : DumbAwareAction(Resources.Icons.LOGO_RED_16x16) { @@ -27,7 +28,37 @@ class CodeLensAction( actionPerformed() } - private fun formatMessage(): String { + private fun replaceVariablesInText( + text: String, + relativePath: String, + cursor: Int?, + codeSelection: String + ): String { + return text + .replace("%CURRENT_FILE%", relativePath) + .replace("%CURSOR_LINE%", cursor?.plus(1)?.toString() ?: "") + .replace("%CODE_SELECTION%", codeSelection) + .replace("%PROMPT_EXPLORATION_TOOLS%", "") + } + + private fun formatMultipleMessagesForCodeLens( + messages: Array, + relativePath: String, + cursor: Int?, + text: String + ): Array { + return messages.map { message -> + if (message.role == "user") { + message.copy( + content = replaceVariablesInText(message.content, relativePath, cursor, text) + ) + } else { + message + } + }.toTypedArray() + } + + private fun formatMessage(): Array { val pos1 = LogicalPosition(line1, 0) val text = editor.document.text.slice( editor.logicalPositionToOffset(pos1) until editor.document.getLineEndOffset(line2) @@ -39,10 +70,9 @@ class CodeLensAction( }.minBy { it.toString().length } } - return contentMsg - .replace("%CURRENT_FILE%", relativePath?.toString() ?: filePath.toString()) - .replace("%CURSOR_LINE%", line1.toString()) - .replace("%CODE_SELECTION%", text) + val formattedMessages = formatMultipleMessagesForCodeLens(messages, relativePath?.toString() ?: filePath.toString(), line1, text); + + return formattedMessages } private val isActionRunning = AtomicBoolean(false) @@ -52,7 +82,7 @@ class CodeLensAction( chat?.activate { RefactAIToolboxPaneFactory.chat?.requestFocus() - RefactAIToolboxPaneFactory.chat?.executeCodeLensCommand(formatMessage(), sendImmediately, openNewTab) + RefactAIToolboxPaneFactory.chat?.executeCodeLensCommand("", formatMessage(), sendImmediately, openNewTab) editor.project?.service()?.addChatStatistic(true, UsageStatistic("openChatByCodelens"), "") } diff --git a/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/ChatPanes.kt b/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/ChatPanes.kt index 2a77fb87..8d066aab 100644 --- a/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/ChatPanes.kt +++ b/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/ChatPanes.kt @@ -3,6 +3,7 @@ package com.smallcloud.refactai.panes.sharedchat import com.intellij.openapi.Disposable import com.intellij.openapi.application.invokeLater import com.intellij.openapi.project.Project +import com.smallcloud.refactai.struct.ChatMessage import java.awt.BorderLayout import javax.swing.JComponent import javax.swing.JPanel @@ -31,8 +32,8 @@ class ChatPanes(val project: Project) : Disposable { return holder } - fun executeCodeLensCommand(command: String, sendImmediately: Boolean, openNewTab: Boolean) { - pane?.executeCodeLensCommand(command, sendImmediately, openNewTab) + fun executeCodeLensCommand(command: String, messages: Array, sendImmediately: Boolean, openNewTab: Boolean) { + pane?.executeCodeLensCommand(command, messages, sendImmediately, openNewTab) } fun requestFocus() { diff --git a/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/Events.kt b/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/Events.kt index 677f730e..2924ed9b 100644 --- a/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/Events.kt +++ b/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/Events.kt @@ -7,6 +7,7 @@ import com.google.gson.JsonElement import com.google.gson.annotations.SerializedName import com.smallcloud.refactai.settings.Host import com.smallcloud.refactai.settings.HostDeserializer +import com.smallcloud.refactai.struct.ChatMessage import java.io.Serializable import java.lang.reflect.Type @@ -366,7 +367,21 @@ class Events { data class CodeLensCommandPayload( val value: String = "", @SerializedName("send_immediately") val sendImmediately: Boolean = false, - ) : Payload() + val messages: Array + ) : Payload() { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as CodeLensCommandPayload + + return messages.contentEquals(other.messages) + } + + override fun hashCode(): Int { + return messages.contentHashCode() + } + } class CodeLensCommand(payload: CodeLensCommandPayload) : ToChat(EventNames.ToChat.CODE_LENS_EXEC, payload) diff --git a/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/SharedChatPane.kt b/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/SharedChatPane.kt index 5a796dcf..87bce638 100644 --- a/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/SharedChatPane.kt +++ b/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/SharedChatPane.kt @@ -40,6 +40,7 @@ import com.smallcloud.refactai.panes.sharedchat.Events.Editor import com.smallcloud.refactai.panes.sharedchat.browser.ChatWebView import com.smallcloud.refactai.settings.AppSettingsConfigurable import com.smallcloud.refactai.settings.Host +import com.smallcloud.refactai.struct.ChatMessage import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -55,7 +56,7 @@ class SharedChatPane(val project: Project) : JPanel(), Disposable { private val editor = Editor(project) private var currentPage: String = "" private var isChatStreaming: Boolean = false - var id: String? = null; + var id: String? = null private val animatedFiles = mutableSetOf() private val scheduler = AppExecutorUtil.createBoundedScheduledExecutorService("SMCRainbowScheduler", 2) @@ -98,7 +99,7 @@ class SharedChatPane(val project: Project) : JPanel(), Disposable { } } - fun executeCodeLensCommand(command: String, sendImmediately: Boolean, openNewTab: Boolean) { + fun executeCodeLensCommand(command: String, messages: Array, sendImmediately: Boolean, openNewTab: Boolean) { if (isChatStreaming) return if (openNewTab || this.currentPage != "chat") { newChat() @@ -109,7 +110,7 @@ class SharedChatPane(val project: Project) : JPanel(), Disposable { return } isChatStreaming = true - this.postMessage(Events.CodeLensCommand(Events.CodeLensCommandPayload(command, sendImmediately))) + this.postMessage(Events.CodeLensCommand(Events.CodeLensCommandPayload(command, sendImmediately, messages))) } private fun sendUserConfig() { @@ -152,7 +153,7 @@ class SharedChatPane(val project: Project) : JPanel(), Disposable { val out = process.getResultStdoutStr().getOrNull() if (out == null) { println("Save btok file output is null") - return; + return } val fileName = out.lines().last() From eea12540976a39845af0ee750c0878727502a040 Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Thu, 9 Jan 2025 11:33:35 +0100 Subject: [PATCH 8/9] feat: adjusted ChatMessage data class & fixed RefactCodeVisionProvider --- .../smallcloud/refactai/code_lens/CodeLensAction.kt | 9 +++++---- .../refactai/code_lens/RefactCodeVisionProvider.kt | 11 +++++++---- .../refactai/panes/sharedchat/SharedChatPane.kt | 2 +- .../com/smallcloud/refactai/struct/ChatMessage.kt | 2 +- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/main/kotlin/com/smallcloud/refactai/code_lens/CodeLensAction.kt b/src/main/kotlin/com/smallcloud/refactai/code_lens/CodeLensAction.kt index f54f567c..dd20b7b6 100644 --- a/src/main/kotlin/com/smallcloud/refactai/code_lens/CodeLensAction.kt +++ b/src/main/kotlin/com/smallcloud/refactai/code_lens/CodeLensAction.kt @@ -47,7 +47,7 @@ class CodeLensAction( cursor: Int?, text: String ): Array { - return messages.map { message -> + val formattedMessages = messages.map { message -> if (message.role == "user") { message.copy( content = replaceVariablesInText(message.content, relativePath, cursor, text) @@ -56,9 +56,10 @@ class CodeLensAction( message } }.toTypedArray() + return formattedMessages } - private fun formatMessage(): Array { + private fun formatMessages(): Array { val pos1 = LogicalPosition(line1, 0) val text = editor.document.text.slice( editor.logicalPositionToOffset(pos1) until editor.document.getLineEndOffset(line2) @@ -82,12 +83,12 @@ class CodeLensAction( chat?.activate { RefactAIToolboxPaneFactory.chat?.requestFocus() - RefactAIToolboxPaneFactory.chat?.executeCodeLensCommand("", formatMessage(), sendImmediately, openNewTab) + RefactAIToolboxPaneFactory.chat?.executeCodeLensCommand("", formatMessages(), sendImmediately, openNewTab) editor.project?.service()?.addChatStatistic(true, UsageStatistic("openChatByCodelens"), "") } // If content is empty, then it's "Open Chat" instruction, selecting range of code in active tab - if (contentMsg.isEmpty() && isActionRunning.compareAndSet(false, true)) { + if (messages.isEmpty() && isActionRunning.compareAndSet(false, true)) { ApplicationManager.getApplication().invokeLater { try { val pos1 = LogicalPosition(line1, 0) diff --git a/src/main/kotlin/com/smallcloud/refactai/code_lens/RefactCodeVisionProvider.kt b/src/main/kotlin/com/smallcloud/refactai/code_lens/RefactCodeVisionProvider.kt index 0f6b86c6..49885806 100644 --- a/src/main/kotlin/com/smallcloud/refactai/code_lens/RefactCodeVisionProvider.kt +++ b/src/main/kotlin/com/smallcloud/refactai/code_lens/RefactCodeVisionProvider.kt @@ -70,16 +70,19 @@ class RefactCodeVisionProvider( val value = allCodeLenses.get(commandKey).asJsonObject val msgs = value.asJsonObject.get("messages").asJsonArray.map { gson.fromJson(it.asJsonObject, ChatMessage::class.java) - }.toList() - val msg = msgs.find { it.role == "user" } + }.toTypedArray() + val userMsg = msgs.find { it.role == "user" } + val sendImmediately = value.asJsonObject.get("auto_submit").asBoolean val openNewTab = value.asJsonObject.get("new_tab")?.asBoolean ?: true - if (msg != null || msgs.isEmpty()) { + + val isValidCodeLen = msgs.isEmpty() || userMsg != null + if (isValidCodeLen) { resCodeLenses.add( CodeLen( range, value.asJsonObject.get("label").asString, - CodeLensAction(editor, line1, line2, msg?.content ?: "", sendImmediately, openNewTab) + CodeLensAction(editor, line1, line2, msgs, sendImmediately, openNewTab) ) ) } diff --git a/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/SharedChatPane.kt b/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/SharedChatPane.kt index 87bce638..3b66d2d3 100644 --- a/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/SharedChatPane.kt +++ b/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/SharedChatPane.kt @@ -104,7 +104,7 @@ class SharedChatPane(val project: Project) : JPanel(), Disposable { if (openNewTab || this.currentPage != "chat") { newChat() } - if (command.isEmpty()) { + if (messages.isEmpty()) { // Just opening a new chat, no codelens execution newChat() return diff --git a/src/main/kotlin/com/smallcloud/refactai/struct/ChatMessage.kt b/src/main/kotlin/com/smallcloud/refactai/struct/ChatMessage.kt index 48e7ad18..ba3b3ddb 100644 --- a/src/main/kotlin/com/smallcloud/refactai/struct/ChatMessage.kt +++ b/src/main/kotlin/com/smallcloud/refactai/struct/ChatMessage.kt @@ -4,5 +4,5 @@ import com.google.gson.annotations.SerializedName data class ChatMessage(val role: String, val content: String, - @SerializedName("tool_call_id") val toolCallId: String, + @SerializedName("tool_call_id") val toolCallId: String?, val usage: String?) \ No newline at end of file From 7c5b726971cd724bc3057a69dafad7498e75e200 Mon Sep 17 00:00:00 2001 From: alashchev17 Date: Fri, 10 Jan 2025 12:45:27 +0100 Subject: [PATCH 9/9] fix: removed command from executeCodeLensCommand action since sending messages directly --- .../com/smallcloud/refactai/code_lens/CodeLensAction.kt | 2 +- .../com/smallcloud/refactai/panes/sharedchat/ChatPanes.kt | 4 ++-- .../smallcloud/refactai/panes/sharedchat/SharedChatPane.kt | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/com/smallcloud/refactai/code_lens/CodeLensAction.kt b/src/main/kotlin/com/smallcloud/refactai/code_lens/CodeLensAction.kt index dd20b7b6..c0fa3f1d 100644 --- a/src/main/kotlin/com/smallcloud/refactai/code_lens/CodeLensAction.kt +++ b/src/main/kotlin/com/smallcloud/refactai/code_lens/CodeLensAction.kt @@ -83,7 +83,7 @@ class CodeLensAction( chat?.activate { RefactAIToolboxPaneFactory.chat?.requestFocus() - RefactAIToolboxPaneFactory.chat?.executeCodeLensCommand("", formatMessages(), sendImmediately, openNewTab) + RefactAIToolboxPaneFactory.chat?.executeCodeLensCommand(formatMessages(), sendImmediately, openNewTab) editor.project?.service()?.addChatStatistic(true, UsageStatistic("openChatByCodelens"), "") } diff --git a/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/ChatPanes.kt b/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/ChatPanes.kt index 8d066aab..a74eb909 100644 --- a/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/ChatPanes.kt +++ b/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/ChatPanes.kt @@ -32,8 +32,8 @@ class ChatPanes(val project: Project) : Disposable { return holder } - fun executeCodeLensCommand(command: String, messages: Array, sendImmediately: Boolean, openNewTab: Boolean) { - pane?.executeCodeLensCommand(command, messages, sendImmediately, openNewTab) + fun executeCodeLensCommand(messages: Array, sendImmediately: Boolean, openNewTab: Boolean) { + pane?.executeCodeLensCommand(messages, sendImmediately, openNewTab) } fun requestFocus() { diff --git a/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/SharedChatPane.kt b/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/SharedChatPane.kt index 3b66d2d3..6da4c49d 100644 --- a/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/SharedChatPane.kt +++ b/src/main/kotlin/com/smallcloud/refactai/panes/sharedchat/SharedChatPane.kt @@ -99,7 +99,7 @@ class SharedChatPane(val project: Project) : JPanel(), Disposable { } } - fun executeCodeLensCommand(command: String, messages: Array, sendImmediately: Boolean, openNewTab: Boolean) { + fun executeCodeLensCommand(messages: Array, sendImmediately: Boolean, openNewTab: Boolean) { if (isChatStreaming) return if (openNewTab || this.currentPage != "chat") { newChat() @@ -110,7 +110,7 @@ class SharedChatPane(val project: Project) : JPanel(), Disposable { return } isChatStreaming = true - this.postMessage(Events.CodeLensCommand(Events.CodeLensCommandPayload(command, sendImmediately, messages))) + this.postMessage(Events.CodeLensCommand(Events.CodeLensCommandPayload("", sendImmediately, messages))) } private fun sendUserConfig() {