From 55df59d03e59cbd8581b0fd3fe1f31b90ef11058 Mon Sep 17 00:00:00 2001 From: Luo Tim Date: Wed, 18 Sep 2024 18:49:19 +0800 Subject: [PATCH 1/4] Refactor Command, DevChatWrapper and handlers to remove dependencies on global currentProject --- .../ai/devchat/core/BaseActionHandler.kt | 2 +- .../kotlin/ai/devchat/core/DevChatWrapper.kt | 22 +++++++++---------- .../core/handlers/InsertCodeRequestHandler.kt | 5 ++--- .../ai/devchat/core/handlers/NewSrcFile.kt | 2 -- .../handlers/ReplaceFileContentHandler.kt | 5 ++--- .../core/handlers/ViewDiffRequestHandler.kt | 3 +-- .../plugin/DevChatToolWindowFactory.kt | 1 - .../devchat/plugin/ToolWindowStateListener.kt | 4 +--- 8 files changed, 17 insertions(+), 27 deletions(-) diff --git a/src/main/kotlin/ai/devchat/core/BaseActionHandler.kt b/src/main/kotlin/ai/devchat/core/BaseActionHandler.kt index 3da1cd5..428a72b 100644 --- a/src/main/kotlin/ai/devchat/core/BaseActionHandler.kt +++ b/src/main/kotlin/ai/devchat/core/BaseActionHandler.kt @@ -13,7 +13,7 @@ abstract class BaseActionHandler( var metadata: JSONObject? = null, var payload: JSONObject? = null ) : ActionHandler { - val wrapper = DevChatWrapper() + val wrapper = DevChatWrapper(project) val jsCallback: String = metadata?.getString("callback") ?: DEFAULT_RESPONSE_FUNC abstract val actionName: String diff --git a/src/main/kotlin/ai/devchat/core/DevChatWrapper.kt b/src/main/kotlin/ai/devchat/core/DevChatWrapper.kt index e388a38..de262e0 100644 --- a/src/main/kotlin/ai/devchat/core/DevChatWrapper.kt +++ b/src/main/kotlin/ai/devchat/core/DevChatWrapper.kt @@ -3,10 +3,10 @@ package ai.devchat.core import ai.devchat.common.Log import ai.devchat.common.Notifier import ai.devchat.common.PathUtils -import ai.devchat.plugin.currentProject import ai.devchat.plugin.ideServerPort import ai.devchat.storage.CONFIG import com.intellij.execution.process.OSProcessUtil.killProcessTree +import com.intellij.openapi.project.Project import com.intellij.util.containers.addIfNotNull import kotlinx.coroutines.* import kotlinx.coroutines.channels.SendChannel @@ -46,10 +46,10 @@ suspend fun executeCommand( } } -class Command(val cmd: MutableList = mutableListOf()) { +class Command(private val workDir: String?, val cmd: MutableList = mutableListOf()) { private val env: MutableMap = mutableMapOf() - constructor(parent: Command) : this() { + constructor(parent: Command) : this(parent.workDir) { cmd.addAll(parent.cmd) env.putAll(parent.env) } @@ -92,11 +92,7 @@ class Command(val cmd: MutableList = mutableListOf()) { val outputLines: MutableList = mutableListOf() val errorLines: MutableList = mutableListOf() val exitCode = runBlocking { - executeCommand( - preparedCommand, - currentProject?.basePath, - env - ).await(outputLines::add, errorLines::add) + executeCommand(preparedCommand, workDir, env).await(outputLines::add, errorLines::add) } val errors = errorLines.joinToString("\n") val outputs = outputLines.joinToString("\n") @@ -105,7 +101,9 @@ class Command(val cmd: MutableList = mutableListOf()) { Log.info("Execution time: ${endTime - startTime} ms") if (exitCode != 0) { - throw CommandExecutionException("Command failure with exit Code: $exitCode, Errors: $errors, Outputs: $outputs") + throw CommandExecutionException( + "Command failure with exit Code: $exitCode, Errors: $errors, Outputs: $outputs" + ) } else { outputs } @@ -138,7 +136,7 @@ class Command(val cmd: MutableList = mutableListOf()) { return CoroutineScope( SupervisorJob() + Dispatchers.Default + exceptionHandler ).actor { - val process = executeCommand(preparedCommand, currentProject?.basePath, env) + val process = executeCommand(preparedCommand, workDir, env) val writer = process.outputStream.bufferedWriter() val errorLines: MutableList = mutableListOf() val deferred = async {process.await(onOutput, errorLines::add)} @@ -183,8 +181,7 @@ class Command(val cmd: MutableList = mutableListOf()) { } } -class DevChatWrapper( -) { +class DevChatWrapper(val project: Project) { private val apiKey get() = CONFIG["providers.devchat.api_key"] as? String private val defaultModel get() = CONFIG["default_model"] as? String @@ -203,6 +200,7 @@ class DevChatWrapper( return v } private val baseCommand get() = Command( + project.basePath, mutableListOf(CONFIG["python_for_chat"] as String, "-m", "devchat") ).addEnv(getEnv()) diff --git a/src/main/kotlin/ai/devchat/core/handlers/InsertCodeRequestHandler.kt b/src/main/kotlin/ai/devchat/core/handlers/InsertCodeRequestHandler.kt index 71da9e9..f5f7b25 100644 --- a/src/main/kotlin/ai/devchat/core/handlers/InsertCodeRequestHandler.kt +++ b/src/main/kotlin/ai/devchat/core/handlers/InsertCodeRequestHandler.kt @@ -2,7 +2,6 @@ package ai.devchat.core.handlers import ai.devchat.core.BaseActionHandler import ai.devchat.core.DevChatActions -import ai.devchat.plugin.currentProject import com.alibaba.fastjson.JSONObject import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.command.CommandProcessor @@ -19,10 +18,10 @@ class InsertCodeRequestHandler(project: Project, requestAction: String, metadata override fun action() { val contentText = payload!!.getString("content") ApplicationManager.getApplication().invokeLater { - val editor = currentProject?.let { FileEditorManager.getInstance(it).selectedTextEditor } + val editor = FileEditorManager.getInstance(project).selectedTextEditor val document = editor!!.document val offset = editor.caretModel.offset - CommandProcessor.getInstance().executeCommand(currentProject, { + CommandProcessor.getInstance().executeCommand(project, { ApplicationManager.getApplication().runWriteAction { document.insertString(offset, contentText) } diff --git a/src/main/kotlin/ai/devchat/core/handlers/NewSrcFile.kt b/src/main/kotlin/ai/devchat/core/handlers/NewSrcFile.kt index 4a3f6b1..5e5c502 100644 --- a/src/main/kotlin/ai/devchat/core/handlers/NewSrcFile.kt +++ b/src/main/kotlin/ai/devchat/core/handlers/NewSrcFile.kt @@ -2,7 +2,6 @@ package ai.devchat.core.handlers import ai.devchat.core.BaseActionHandler import ai.devchat.core.DevChatActions -import ai.devchat.plugin.currentProject import com.alibaba.fastjson.JSONObject import com.intellij.lang.Language import com.intellij.openapi.application.ApplicationManager @@ -24,7 +23,6 @@ class NewSrcFile(project: Project, requestAction: String, metadata: JSONObject?, val content = payload!!.getString("content") val language = payload!!.getString("language") runInEdt { - val project = currentProject ?: return@runInEdt val dir = FileEditorManager.getInstance(project).selectedEditor?.file?.parent ?: return@runInEdt ApplicationManager.getApplication().runWriteAction { val psiDirectory = PsiManager.getInstance(project).findDirectory(dir) ?: return@runWriteAction diff --git a/src/main/kotlin/ai/devchat/core/handlers/ReplaceFileContentHandler.kt b/src/main/kotlin/ai/devchat/core/handlers/ReplaceFileContentHandler.kt index 2178659..d59c36c 100644 --- a/src/main/kotlin/ai/devchat/core/handlers/ReplaceFileContentHandler.kt +++ b/src/main/kotlin/ai/devchat/core/handlers/ReplaceFileContentHandler.kt @@ -2,7 +2,6 @@ package ai.devchat.core.handlers import ai.devchat.core.BaseActionHandler import ai.devchat.core.DevChatActions -import ai.devchat.plugin.currentProject import com.alibaba.fastjson.JSONObject import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.command.CommandProcessor @@ -19,9 +18,9 @@ class ReplaceFileContentHandler(project: Project, requestAction: String, metadat override fun action() { val newFileContent = payload!!.getString("content") ApplicationManager.getApplication().invokeLater { - val editor = FileEditorManager.getInstance(currentProject!!).selectedTextEditor + val editor = FileEditorManager.getInstance(project).selectedTextEditor val document = editor!!.document - CommandProcessor.getInstance().executeCommand(currentProject, { + CommandProcessor.getInstance().executeCommand(project, { ApplicationManager.getApplication().runWriteAction { document.setText(newFileContent) } }, "ReplaceFileContentHandler", null) diff --git a/src/main/kotlin/ai/devchat/core/handlers/ViewDiffRequestHandler.kt b/src/main/kotlin/ai/devchat/core/handlers/ViewDiffRequestHandler.kt index 4e28728..10bdb13 100644 --- a/src/main/kotlin/ai/devchat/core/handlers/ViewDiffRequestHandler.kt +++ b/src/main/kotlin/ai/devchat/core/handlers/ViewDiffRequestHandler.kt @@ -3,7 +3,6 @@ package ai.devchat.core.handlers import ai.devchat.core.BaseActionHandler import ai.devchat.core.DevChatActions import ai.devchat.plugin.DiffViewerDialog -import ai.devchat.plugin.currentProject import com.alibaba.fastjson.JSONObject import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.fileEditor.FileEditorManager @@ -19,7 +18,7 @@ class ViewDiffRequestHandler(project: Project, requestAction: String, metadata: override fun action() { val diffContent = payload!!.getString("content") ApplicationManager.getApplication().invokeLater { - val editor = FileEditorManager.getInstance(currentProject!!).selectedTextEditor + val editor = FileEditorManager.getInstance(project).selectedTextEditor ?: // Handle the case when no editor is opened return@invokeLater DiffViewerDialog(editor, diffContent).show() diff --git a/src/main/kotlin/ai/devchat/plugin/DevChatToolWindowFactory.kt b/src/main/kotlin/ai/devchat/plugin/DevChatToolWindowFactory.kt index c9c1e03..91bcbde 100644 --- a/src/main/kotlin/ai/devchat/plugin/DevChatToolWindowFactory.kt +++ b/src/main/kotlin/ai/devchat/plugin/DevChatToolWindowFactory.kt @@ -25,7 +25,6 @@ class DevChatToolWindowFactory : ToolWindowFactory, DumbAware, Disposable { var browser: Browser? = null override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) { - currentProject = project browser = Browser(project) val panel = JPanel(BorderLayout()) if (!JBCefApp.isSupported()) { diff --git a/src/main/kotlin/ai/devchat/plugin/ToolWindowStateListener.kt b/src/main/kotlin/ai/devchat/plugin/ToolWindowStateListener.kt index b8f5b0d..760de8b 100644 --- a/src/main/kotlin/ai/devchat/plugin/ToolWindowStateListener.kt +++ b/src/main/kotlin/ai/devchat/plugin/ToolWindowStateListener.kt @@ -36,9 +36,7 @@ class ToolWindowStateListener : ToolWindowManagerListener { ToolWindowManagerListener.ToolWindowManagerEventType.ActivateToolWindow -> { val jsFocus = "document.getElementsByClassName('mantine-Input-input mantine-Textarea-input')[0].focus();" toolWindowManager.getToolWindow(DevChatBundle.message("plugin.id"))?.project?.let { project -> - project.getService(DevChatBrowserService::class.java).browser?.let {browser -> - browser.jbCefBrowser.cefBrowser.executeJavaScript(jsFocus, "", 0) - } + project.getService(DevChatBrowserService::class.java).browser?.jbCefBrowser?.cefBrowser?.executeJavaScript(jsFocus, "", 0) } DevChatState.instance.lastToolWindowState = ToolWindowState.SHOWN.name } From d4f30df25db2b828f0d3e240ddedaccc12e2734e Mon Sep 17 00:00:00 2001 From: Luo Tim Date: Wed, 18 Sep 2024 19:52:52 +0800 Subject: [PATCH 2/4] Refactor LocalService & DevChatClient to bind them to lifecycle of project --- .../kotlin/ai/devchat/common/PathUtils.kt | 1 - .../ai/devchat/core/BaseActionHandler.kt | 4 +++- .../kotlin/ai/devchat/core/DevChatClient.kt | 22 +++++++++---------- .../DeleteLastConversationRequestHandler.kt | 3 +-- .../handlers/DeleteTopicRequestHandler.kt | 3 +-- .../handlers/ListCommandsRequestHandler.kt | 5 ++--- .../core/handlers/ListTopicsRequestHandler.kt | 3 +-- .../LoadConversationRequestHandler.kt | 3 +-- .../handlers/SendMessageRequestHandler.kt | 7 +++--- .../devchat/installer/DevChatSetupThread.kt | 8 ++++--- .../plugin/DevChatToolWindowFactory.kt | 18 +++++++++++---- .../kotlin/ai/devchat/plugin/IDEServer.kt | 3 ++- .../kotlin/ai/devchat/plugin/LocalService.kt | 21 +++++++++--------- 13 files changed, 55 insertions(+), 46 deletions(-) diff --git a/src/main/kotlin/ai/devchat/common/PathUtils.kt b/src/main/kotlin/ai/devchat/common/PathUtils.kt index b244f1f..b1aaec9 100644 --- a/src/main/kotlin/ai/devchat/common/PathUtils.kt +++ b/src/main/kotlin/ai/devchat/common/PathUtils.kt @@ -8,7 +8,6 @@ import java.nio.file.attribute.BasicFileAttributes object PathUtils { - val workspace: String? = currentProject?.basePath val workPath: String = Paths.get(System.getProperty("user.home"), ".chat").toString() val workflowPath: String = Paths.get(workPath, "scripts").toString() val sitePackagePath: String = Paths.get(workPath, "site-packages").toString() diff --git a/src/main/kotlin/ai/devchat/core/BaseActionHandler.kt b/src/main/kotlin/ai/devchat/core/BaseActionHandler.kt index 428a72b..902808c 100644 --- a/src/main/kotlin/ai/devchat/core/BaseActionHandler.kt +++ b/src/main/kotlin/ai/devchat/core/BaseActionHandler.kt @@ -2,6 +2,7 @@ package ai.devchat.core import ai.devchat.common.Log import ai.devchat.plugin.DevChatBrowserService +import ai.devchat.plugin.DevChatService import com.alibaba.fastjson.JSONObject import com.intellij.openapi.project.Project @@ -14,7 +15,8 @@ abstract class BaseActionHandler( var payload: JSONObject? = null ) : ActionHandler { val wrapper = DevChatWrapper(project) - val jsCallback: String = metadata?.getString("callback") ?: DEFAULT_RESPONSE_FUNC + val client: DevChatClient = project.getService(DevChatService::class.java).client!! + private val jsCallback: String = metadata?.getString("callback") ?: DEFAULT_RESPONSE_FUNC abstract val actionName: String diff --git a/src/main/kotlin/ai/devchat/core/DevChatClient.kt b/src/main/kotlin/ai/devchat/core/DevChatClient.kt index f577cdb..3cc3765 100644 --- a/src/main/kotlin/ai/devchat/core/DevChatClient.kt +++ b/src/main/kotlin/ai/devchat/core/DevChatClient.kt @@ -2,7 +2,7 @@ package ai.devchat.core import ai.devchat.common.Log import ai.devchat.common.PathUtils -import ai.devchat.plugin.localServicePort +import com.intellij.openapi.project.Project import kotlinx.coroutines.* import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow @@ -27,7 +27,6 @@ import java.time.Instant import kotlin.system.measureTimeMillis - inline fun T.asMap(): Map where T : @Serializable Any { val json = Json { encodeDefaults = true } val jsonString = json.encodeToString(serializer(),this) @@ -42,7 +41,7 @@ data class ChatRequest( @SerialName("api_base") val apiBase: String, val parent: String?, val context: List?, - val workspace: String? = PathUtils.workspace, + val workspace: String? = null, @Transient val contextContents: List? = null, @Transient val response: ChatResponse? = null, ) @@ -209,10 +208,11 @@ fun timeThis(block: suspend () -> Unit) { } } -class DevChatClient { +class DevChatClient(val project: Project, private val localServicePort: Int) { private val scope: CoroutineScope = CoroutineScope(Dispatchers.IO) private val baseURL get() = "http://localhost:$localServicePort" private var job: Job? = null + private val workspace: String? = project.basePath companion object { const val DEFAULT_LOG_MAX_COUNT = 10000 @@ -350,7 +350,7 @@ class DevChatClient { } fun insertLog(logEntry: LogEntry): LogInsertRes? { - val body = mutableMapOf("workspace" to PathUtils.workspace) + val body = mutableMapOf("workspace" to workspace) val jsonData = json.encodeToString(serializer(), logEntry) if (jsonData.length <= LOG_RAW_DATA_SIZE_LIMIT) { body["jsondata"] = jsonData @@ -369,7 +369,7 @@ class DevChatClient { } fun deleteLog(logHash: String): LogDeleteRes? { return post("/logs/delete", mapOf( - "workspace" to PathUtils.workspace, + "workspace" to workspace, "hash" to logHash )) } @@ -377,14 +377,14 @@ class DevChatClient { return get>("/topics/$topicRootHash/logs", mapOf( "limit" to limit, "offset" to offset, - "workspace" to PathUtils.workspace, + "workspace" to workspace, )) ?: emptyList() } fun getTopics(offset: Int = 0, limit: Int = DEFAULT_LOG_MAX_COUNT): List { val queryParams = mapOf( "limit" to limit, "offset" to offset, - "workspace" to PathUtils.workspace, + "workspace" to workspace, ) return get?>("/topics", queryParams).orEmpty() } @@ -392,7 +392,7 @@ class DevChatClient { fun deleteTopic(topicRootHash: String) { val response: Map? = post("/topics/delete", mapOf( "topic_hash" to topicRootHash, - "workspace" to PathUtils.workspace, + "workspace" to workspace, )) Log.info("deleteTopic response data: $response") } @@ -401,6 +401,4 @@ class DevChatClient { job?.cancel() job = null } -} - -val DC_CLIENT: DevChatClient = DevChatClient() \ No newline at end of file +} \ No newline at end of file diff --git a/src/main/kotlin/ai/devchat/core/handlers/DeleteLastConversationRequestHandler.kt b/src/main/kotlin/ai/devchat/core/handlers/DeleteLastConversationRequestHandler.kt index 7b84395..bf76cb8 100644 --- a/src/main/kotlin/ai/devchat/core/handlers/DeleteLastConversationRequestHandler.kt +++ b/src/main/kotlin/ai/devchat/core/handlers/DeleteLastConversationRequestHandler.kt @@ -1,7 +1,6 @@ package ai.devchat.core.handlers import ai.devchat.core.BaseActionHandler -import ai.devchat.core.DC_CLIENT import ai.devchat.core.DevChatActions import com.alibaba.fastjson.JSONObject import com.intellij.openapi.project.Project @@ -15,7 +14,7 @@ class DeleteLastConversationRequestHandler(project: Project, requestAction: Stri override val actionName: String = DevChatActions.DELETE_LAST_CONVERSATION_RESPONSE override fun action() { val promptHash = payload!!.getString("promptHash") - DC_CLIENT.deleteLog(promptHash) + client.deleteLog(promptHash) send(payload = mapOf("promptHash" to promptHash)) } } diff --git a/src/main/kotlin/ai/devchat/core/handlers/DeleteTopicRequestHandler.kt b/src/main/kotlin/ai/devchat/core/handlers/DeleteTopicRequestHandler.kt index 1e7b5c8..9f89362 100644 --- a/src/main/kotlin/ai/devchat/core/handlers/DeleteTopicRequestHandler.kt +++ b/src/main/kotlin/ai/devchat/core/handlers/DeleteTopicRequestHandler.kt @@ -1,7 +1,6 @@ package ai.devchat.core.handlers import ai.devchat.core.BaseActionHandler -import ai.devchat.core.DC_CLIENT import ai.devchat.core.DevChatActions import com.alibaba.fastjson.JSONObject import com.intellij.openapi.project.Project @@ -15,7 +14,7 @@ class DeleteTopicRequestHandler(project: Project, requestAction: String, metadat override val actionName: String = DevChatActions.DELETE_TOPIC_RESPONSE override fun action() { val topicHash = payload!!.getString("topicHash") - DC_CLIENT.deleteTopic(topicHash) + client.deleteTopic(topicHash) send(payload = mapOf("topicHash" to topicHash)) } } diff --git a/src/main/kotlin/ai/devchat/core/handlers/ListCommandsRequestHandler.kt b/src/main/kotlin/ai/devchat/core/handlers/ListCommandsRequestHandler.kt index e3ab496..9438678 100644 --- a/src/main/kotlin/ai/devchat/core/handlers/ListCommandsRequestHandler.kt +++ b/src/main/kotlin/ai/devchat/core/handlers/ListCommandsRequestHandler.kt @@ -1,7 +1,6 @@ package ai.devchat.core.handlers import ai.devchat.core.BaseActionHandler -import ai.devchat.core.DC_CLIENT import ai.devchat.core.DevChatActions import com.alibaba.fastjson.JSONObject import com.intellij.openapi.project.Project @@ -15,8 +14,8 @@ class ListCommandsRequestHandler(project: Project, requestAction: String, metada ) { override val actionName: String = DevChatActions.LIST_COMMANDS_RESPONSE override fun action() { - val recommendedWorkflows = DC_CLIENT.getWorkflowConfig()?.recommend?.workflows.orEmpty() - val indexedCommands = DC_CLIENT.getWorkflowList()?.map { + val recommendedWorkflows = client.getWorkflowConfig()?.recommend?.workflows.orEmpty() + val indexedCommands = client.getWorkflowList()?.map { val commandName = it.name mapOf( "name" to it.name, diff --git a/src/main/kotlin/ai/devchat/core/handlers/ListTopicsRequestHandler.kt b/src/main/kotlin/ai/devchat/core/handlers/ListTopicsRequestHandler.kt index b8b174b..1ba2414 100644 --- a/src/main/kotlin/ai/devchat/core/handlers/ListTopicsRequestHandler.kt +++ b/src/main/kotlin/ai/devchat/core/handlers/ListTopicsRequestHandler.kt @@ -1,7 +1,6 @@ package ai.devchat.core.handlers import ai.devchat.core.BaseActionHandler -import ai.devchat.core.DC_CLIENT import ai.devchat.core.DevChatActions import com.alibaba.fastjson.JSONObject import com.intellij.openapi.project.Project @@ -15,7 +14,7 @@ class ListTopicsRequestHandler(project: Project, requestAction: String, metadata ) { override val actionName: String = DevChatActions.LIST_TOPICS_RESPONSE override fun action() { - val topics = DC_CLIENT.getTopics().map { + val topics = client.getTopics().map { val request = it.rootPromptRequest val response = it.rootPromptResponse mapOf( diff --git a/src/main/kotlin/ai/devchat/core/handlers/LoadConversationRequestHandler.kt b/src/main/kotlin/ai/devchat/core/handlers/LoadConversationRequestHandler.kt index 664cb90..de547e0 100644 --- a/src/main/kotlin/ai/devchat/core/handlers/LoadConversationRequestHandler.kt +++ b/src/main/kotlin/ai/devchat/core/handlers/LoadConversationRequestHandler.kt @@ -1,7 +1,6 @@ package ai.devchat.core.handlers import ai.devchat.core.BaseActionHandler -import ai.devchat.core.DC_CLIENT import ai.devchat.core.DevChatActions import ai.devchat.storage.ActiveConversation import com.alibaba.fastjson.JSONObject @@ -22,7 +21,7 @@ class LoadConversationRequestHandler(project: Project, requestAction: String, me topicHash.isNullOrEmpty() -> ActiveConversation.reset() topicHash == ActiveConversation.topic -> res["reset"] = false else -> { - val logs = DC_CLIENT.getTopicLogs(topicHash) + val logs = client.getTopicLogs(topicHash) ActiveConversation.reset(topicHash, logs) } } diff --git a/src/main/kotlin/ai/devchat/core/handlers/SendMessageRequestHandler.kt b/src/main/kotlin/ai/devchat/core/handlers/SendMessageRequestHandler.kt index e7d425a..5c84ab6 100644 --- a/src/main/kotlin/ai/devchat/core/handlers/SendMessageRequestHandler.kt +++ b/src/main/kotlin/ai/devchat/core/handlers/SendMessageRequestHandler.kt @@ -48,11 +48,12 @@ class SendMessageRequestHandler(project: Project, requestAction: String, metadat apiBase = CONFIG["providers.devchat.api_base"] as String, parent=parent, context = contextTempFilePaths, + workspace = project.basePath, response = ChatResponse(), contextContents = contextContents, ) - DC_CLIENT.message( + client.message( chatRequest, dataHandler(chatRequest), ::errorHandler, @@ -102,7 +103,7 @@ class SendMessageRequestHandler(project: Project, requestAction: String, metadat return { exitCode: Int -> when(exitCode) { 0 -> { - val entry = DC_CLIENT.insertLog( + val entry = client.insertLog( LogEntry( chatRequest.modelName, chatRequest.parent, @@ -115,7 +116,7 @@ class SendMessageRequestHandler(project: Project, requestAction: String, metadat promptCallback(response) val currentTopic = ActiveConversation.topic ?: response.promptHash!! - val logs = DC_CLIENT.getTopicLogs(currentTopic, 0, 1) + val logs = client.getTopicLogs(currentTopic, 0, 1) if (currentTopic == ActiveConversation.topic) { ActiveConversation.addMessage(logs.first()) diff --git a/src/main/kotlin/ai/devchat/installer/DevChatSetupThread.kt b/src/main/kotlin/ai/devchat/installer/DevChatSetupThread.kt index 03158b4..6fadb7a 100644 --- a/src/main/kotlin/ai/devchat/installer/DevChatSetupThread.kt +++ b/src/main/kotlin/ai/devchat/installer/DevChatSetupThread.kt @@ -2,8 +2,9 @@ package ai.devchat.installer import ai.devchat.common.* import ai.devchat.common.Constants.ASSISTANT_NAME_EN -import ai.devchat.core.DC_CLIENT +import ai.devchat.core.DevChatClient import ai.devchat.plugin.DevChatBrowserService +import ai.devchat.plugin.DevChatService import ai.devchat.plugin.DevChatToolWindowFactory import ai.devchat.storage.CONFIG import ai.devchat.storage.DevChatState @@ -73,8 +74,9 @@ class DevChatSetupThread(val project: Project) : Thread() { DevChatToolWindowFactory.pythonReady = true try { - DC_CLIENT.updateWorkflows() - DC_CLIENT.updateCustomWorkflows() + val dcClient: DevChatClient = project.getService(DevChatService::class.java).client!! + dcClient.updateWorkflows() + dcClient.updateCustomWorkflows() } catch (e: Exception) { Log.warn("Failed to update workflows: $e") } diff --git a/src/main/kotlin/ai/devchat/plugin/DevChatToolWindowFactory.kt b/src/main/kotlin/ai/devchat/plugin/DevChatToolWindowFactory.kt index 91bcbde..461d3a9 100644 --- a/src/main/kotlin/ai/devchat/plugin/DevChatToolWindowFactory.kt +++ b/src/main/kotlin/ai/devchat/plugin/DevChatToolWindowFactory.kt @@ -1,9 +1,11 @@ package ai.devchat.plugin import ai.devchat.common.Log +import ai.devchat.core.DevChatClient import ai.devchat.core.DevChatWrapper import ai.devchat.installer.DevChatSetupThread import com.intellij.openapi.Disposable +import com.intellij.openapi.components.Service import com.intellij.openapi.project.DumbAware import com.intellij.openapi.project.Project import com.intellij.openapi.util.Disposer @@ -18,6 +20,13 @@ import javax.swing.JLabel import javax.swing.JPanel import javax.swing.SwingConstants +@Service(Service.Level.PROJECT) +class DevChatService(project: Project) { + var browser: Browser? = null + var localServicePort: Int? = null + var client: DevChatClient? = null +} + class DevChatToolWindowFactory : ToolWindowFactory, DumbAware, Disposable { private var ideService: IDEServer? = null private var localService: LocalService? = null @@ -50,13 +59,16 @@ class DevChatToolWindowFactory : ToolWindowFactory, DumbAware, Disposable { delay(100) ensureActive() } - localService = LocalService().start() + localService = LocalService(project).start() awaitCancellation() } finally { localService?.stop() } } - project.getService(DevChatBrowserService::class.java).browser = browser + val devChatService = project.getService(DevChatService::class.java) + devChatService.browser = browser + devChatService.localServicePort = localService?.port + devChatService.client = DevChatClient(project, localService!!.port!!) } override fun dispose() { @@ -71,5 +83,3 @@ class DevChatToolWindowFactory : ToolWindowFactory, DumbAware, Disposable { var pythonReady: Boolean = false } } - -var currentProject: Project? = null diff --git a/src/main/kotlin/ai/devchat/plugin/IDEServer.kt b/src/main/kotlin/ai/devchat/plugin/IDEServer.kt index e8db47b..5312538 100644 --- a/src/main/kotlin/ai/devchat/plugin/IDEServer.kt +++ b/src/main/kotlin/ai/devchat/plugin/IDEServer.kt @@ -154,7 +154,8 @@ class IDEServer(private var project: Project) { } post("/get_local_service_port") { - call.respond(Result(localServicePort)) + val devChatService = project.getService(DevChatService::class.java) + call.respond(Result(devChatService.localServicePort)) } post("/ide_language") { diff --git a/src/main/kotlin/ai/devchat/plugin/LocalService.kt b/src/main/kotlin/ai/devchat/plugin/LocalService.kt index e3a9308..5f081fe 100644 --- a/src/main/kotlin/ai/devchat/plugin/LocalService.kt +++ b/src/main/kotlin/ai/devchat/plugin/LocalService.kt @@ -9,26 +9,29 @@ import com.intellij.execution.process.OSProcessHandler import com.intellij.execution.process.OSProcessUtil.killProcessTree import com.intellij.execution.process.ProcessAdapter import com.intellij.execution.process.ProcessEvent +import com.intellij.openapi.project.Project import com.intellij.openapi.util.Key import com.intellij.util.io.BaseOutputReader import java.net.ServerSocket -class LocalService { +class LocalService(project: Project) { + var port: Int? = null + val workspace: String? = project.basePath private var processHandler: OSProcessHandler? = null private var isShutdownHookRegistered = false fun start(): LocalService { ServerSocket(0).use { - localServicePort = it.localPort + port = it.localPort } val commandLine = GeneralCommandLine() .withExePath(CONFIG["python_for_chat"] as String) .withParameters(PathUtils.localServicePath) - .withWorkDirectory(PathUtils.workspace) + .withWorkDirectory(workspace) .withEnvironment("PYTHONPATH", PathUtils.pythonPath) - .withEnvironment("DC_SVC_PORT", localServicePort.toString()) - .withEnvironment("DC_SVC_WORKSPACE", PathUtils.workspace ?: "") + .withEnvironment("DC_SVC_PORT", port.toString()) + .withEnvironment("DC_SVC_WORKSPACE", workspace ?: "") processHandler = object: OSProcessHandler(commandLine) { override fun readerOptions(): BaseOutputReader.Options { return BaseOutputReader.Options.forMostlySilentProcess() @@ -52,8 +55,8 @@ class LocalService { } processHandler?.startNotify() - Log.info("Local service started on port: $localServicePort") - Notifier.info("Local service started at $localServicePort.") + Log.info("Local service started on port: $port") + Notifier.info("Local service started at $port.") return this } @@ -71,6 +74,4 @@ class LocalService { processHandler = null } ?: Log.info("Local service is not running") } -} - -var localServicePort: Int? = null +} \ No newline at end of file From bd4e20e8f908fc4f59146416d19423a589285b6b Mon Sep 17 00:00:00 2001 From: Luo Tim Date: Thu, 19 Sep 2024 02:31:57 +0800 Subject: [PATCH 3/4] Remove global loaded & bind the value to project --- .../kotlin/ai/devchat/plugin/IDELifecycleListener.kt | 11 ----------- .../plugin/actions/AddToDevChatEditorAction.kt | 5 +++-- .../devchat/plugin/actions/AddToDevChatFileAction.kt | 5 +++-- .../ai/devchat/plugin/actions/AskIssueIntention.kt | 5 +++-- .../kotlin/ai/devchat/plugin/actions/DocComments.kt | 5 +++-- .../kotlin/ai/devchat/plugin/actions/ExplainCode.kt | 5 +++-- src/main/kotlin/ai/devchat/plugin/actions/Fix.kt | 5 +++-- .../ai/devchat/plugin/actions/FixIssueIntention.kt | 5 +++-- .../ai/devchat/plugin/hints/ChatCVProviderBase.kt | 5 +++-- src/main/resources/META-INF/plugin.xml | 6 +++--- 10 files changed, 27 insertions(+), 30 deletions(-) delete mode 100644 src/main/kotlin/ai/devchat/plugin/IDELifecycleListener.kt diff --git a/src/main/kotlin/ai/devchat/plugin/IDELifecycleListener.kt b/src/main/kotlin/ai/devchat/plugin/IDELifecycleListener.kt deleted file mode 100644 index 1d1209f..0000000 --- a/src/main/kotlin/ai/devchat/plugin/IDELifecycleListener.kt +++ /dev/null @@ -1,11 +0,0 @@ -package ai.devchat.plugin - -import ai.devchat.core.DevChatWrapper -import com.intellij.ide.AppLifecycleListener - -class IDELifecycleListener: AppLifecycleListener { - override fun appWillBeClosed(isRestart: Boolean) { - super.appWillBeClosed(isRestart) - DevChatWrapper.activeChannel?.close() - } -} \ No newline at end of file diff --git a/src/main/kotlin/ai/devchat/plugin/actions/AddToDevChatEditorAction.kt b/src/main/kotlin/ai/devchat/plugin/actions/AddToDevChatEditorAction.kt index 150e5b2..2710d31 100644 --- a/src/main/kotlin/ai/devchat/plugin/actions/AddToDevChatEditorAction.kt +++ b/src/main/kotlin/ai/devchat/plugin/actions/AddToDevChatEditorAction.kt @@ -2,7 +2,7 @@ package ai.devchat.plugin.actions import ai.devchat.common.Constants.ASSISTANT_NAME_ZH import ai.devchat.common.DevChatBundle -import ai.devchat.plugin.DevChatToolWindowFactory +import ai.devchat.plugin.DevChatService import ai.devchat.storage.CONFIG import com.intellij.openapi.actionSystem.ActionUpdateThread import com.intellij.openapi.actionSystem.AnAction @@ -44,7 +44,8 @@ class AddToDevChatEditorAction : AnAction() { val startOffset = selectionModel.selectionStart val document = editor.document val startLine = document.getLineNumber(startOffset) + 1 - addToDevChatAction!!.execute(relativePath, selectedText, language, startLine, !DevChatToolWindowFactory.loaded) + val uiLoaded = project.getService(DevChatService::class.java).uiLoaded + addToDevChatAction!!.execute(relativePath, selectedText, language, startLine, !uiLoaded) } } } diff --git a/src/main/kotlin/ai/devchat/plugin/actions/AddToDevChatFileAction.kt b/src/main/kotlin/ai/devchat/plugin/actions/AddToDevChatFileAction.kt index 945a618..6f3beea 100644 --- a/src/main/kotlin/ai/devchat/plugin/actions/AddToDevChatFileAction.kt +++ b/src/main/kotlin/ai/devchat/plugin/actions/AddToDevChatFileAction.kt @@ -2,7 +2,7 @@ package ai.devchat.plugin.actions import ai.devchat.common.Constants.ASSISTANT_NAME_ZH import ai.devchat.common.DevChatBundle -import ai.devchat.plugin.DevChatToolWindowFactory +import ai.devchat.plugin.DevChatService import ai.devchat.storage.CONFIG import com.intellij.openapi.actionSystem.ActionUpdateThread import com.intellij.openapi.actionSystem.AnAction @@ -43,7 +43,8 @@ class AddToDevChatFileAction : AnAction() { ToolWindowManager.getInstance(project).getToolWindow(ASSISTANT_NAME_ZH)?.show { val bytes = virtualFile.contentsToByteArray() val content = String(bytes, StandardCharsets.UTF_8) - addToDevChatAction!!.execute(relativePath, content, language, 0, !DevChatToolWindowFactory.loaded) + val uiLoaded = project.getService(DevChatService::class.java).uiLoaded + addToDevChatAction!!.execute(relativePath, content, language, 0, !uiLoaded) } } catch (ex: IOException) { ex.printStackTrace() diff --git a/src/main/kotlin/ai/devchat/plugin/actions/AskIssueIntention.kt b/src/main/kotlin/ai/devchat/plugin/actions/AskIssueIntention.kt index 0d7406f..19dd4fb 100644 --- a/src/main/kotlin/ai/devchat/plugin/actions/AskIssueIntention.kt +++ b/src/main/kotlin/ai/devchat/plugin/actions/AskIssueIntention.kt @@ -4,7 +4,7 @@ import ai.devchat.common.Constants.ASSISTANT_NAME_EN import ai.devchat.common.Constants.ASSISTANT_NAME_ZH import ai.devchat.core.DevChatActions import ai.devchat.core.handlers.SendUserMessageHandler -import ai.devchat.plugin.DevChatToolWindowFactory +import ai.devchat.plugin.DevChatService import com.alibaba.fastjson.JSONObject import com.intellij.codeInsight.intention.IntentionAction import com.intellij.codeInsight.intention.PriorityAction @@ -29,7 +29,8 @@ class AskIssueIntention : IntentionAction, PriorityAction { val payload = JSONObject(mapOf("message" to "/ask_issue")) ToolWindowManager.getInstance(project).getToolWindow(ASSISTANT_NAME_ZH)?.show { - if (DevChatToolWindowFactory.loaded) { + val uiLoaded = project.getService(DevChatService::class.java).uiLoaded + if (uiLoaded) { SendUserMessageHandler(project, DevChatActions.SEND_USER_MESSAGE_REQUEST,null, payload).executeAction() } else { SendUserMessageHandler.cache = payload diff --git a/src/main/kotlin/ai/devchat/plugin/actions/DocComments.kt b/src/main/kotlin/ai/devchat/plugin/actions/DocComments.kt index d3caa7b..2063a49 100644 --- a/src/main/kotlin/ai/devchat/plugin/actions/DocComments.kt +++ b/src/main/kotlin/ai/devchat/plugin/actions/DocComments.kt @@ -4,7 +4,7 @@ import ai.devchat.common.Constants.ASSISTANT_NAME_ZH import ai.devchat.common.DevChatBundle import ai.devchat.core.DevChatActions import ai.devchat.core.handlers.SendUserMessageHandler -import ai.devchat.plugin.DevChatToolWindowFactory +import ai.devchat.plugin.DevChatService import ai.devchat.storage.CONFIG import com.alibaba.fastjson.JSONObject import com.intellij.openapi.actionSystem.ActionUpdateThread @@ -26,7 +26,8 @@ class DocComments : AnAction() { if (editor != null) { ToolWindowManager.getInstance(editor.project!!).getToolWindow(ASSISTANT_NAME_ZH)?.show { val payload = JSONObject(mapOf("message" to "/comments")) - if (DevChatToolWindowFactory.loaded) { + val uiLoaded = editor.project!!.getService(DevChatService::class.java).uiLoaded + if (uiLoaded) { SendUserMessageHandler(e.project!!, DevChatActions.SEND_USER_MESSAGE_REQUEST,null, payload).executeAction() } else { SendUserMessageHandler.cache = payload diff --git a/src/main/kotlin/ai/devchat/plugin/actions/ExplainCode.kt b/src/main/kotlin/ai/devchat/plugin/actions/ExplainCode.kt index 71761e4..e90a8e9 100644 --- a/src/main/kotlin/ai/devchat/plugin/actions/ExplainCode.kt +++ b/src/main/kotlin/ai/devchat/plugin/actions/ExplainCode.kt @@ -4,7 +4,7 @@ import ai.devchat.common.Constants.ASSISTANT_NAME_ZH import ai.devchat.common.DevChatBundle import ai.devchat.core.DevChatActions import ai.devchat.core.handlers.SendUserMessageHandler -import ai.devchat.plugin.DevChatToolWindowFactory +import ai.devchat.plugin.DevChatService import ai.devchat.storage.CONFIG import com.alibaba.fastjson.JSONObject import com.intellij.openapi.actionSystem.ActionUpdateThread @@ -26,7 +26,8 @@ class ExplainCode : AnAction() { if (editor != null) { ToolWindowManager.getInstance(editor.project!!).getToolWindow(ASSISTANT_NAME_ZH)?.show { val payload = JSONObject(mapOf("message" to "/explain")) - if (DevChatToolWindowFactory.loaded) { + val uiLoaded = editor.project!!.getService(DevChatService::class.java).uiLoaded + if (uiLoaded) { SendUserMessageHandler(e.project!!, DevChatActions.SEND_USER_MESSAGE_REQUEST,null, payload).executeAction() } else { SendUserMessageHandler.cache = payload diff --git a/src/main/kotlin/ai/devchat/plugin/actions/Fix.kt b/src/main/kotlin/ai/devchat/plugin/actions/Fix.kt index 46c76af..73976fd 100644 --- a/src/main/kotlin/ai/devchat/plugin/actions/Fix.kt +++ b/src/main/kotlin/ai/devchat/plugin/actions/Fix.kt @@ -4,7 +4,7 @@ import ai.devchat.common.Constants.ASSISTANT_NAME_ZH import ai.devchat.common.DevChatBundle import ai.devchat.core.DevChatActions import ai.devchat.core.handlers.SendUserMessageHandler -import ai.devchat.plugin.DevChatToolWindowFactory +import ai.devchat.plugin.DevChatService import ai.devchat.storage.CONFIG import com.alibaba.fastjson.JSONObject import com.intellij.openapi.actionSystem.ActionUpdateThread @@ -26,7 +26,8 @@ class Fix : AnAction() { if (editor != null) { ToolWindowManager.getInstance(editor.project!!).getToolWindow(ASSISTANT_NAME_ZH)?.show { val payload = JSONObject(mapOf("message" to "/fix")) - if (DevChatToolWindowFactory.loaded) { + val uiLoaded = editor.project!!.getService(DevChatService::class.java).uiLoaded + if (uiLoaded) { SendUserMessageHandler(e.project!!, DevChatActions.SEND_USER_MESSAGE_REQUEST,null, payload).executeAction() } else { SendUserMessageHandler.cache = payload diff --git a/src/main/kotlin/ai/devchat/plugin/actions/FixIssueIntention.kt b/src/main/kotlin/ai/devchat/plugin/actions/FixIssueIntention.kt index 08a362b..6dc4a56 100644 --- a/src/main/kotlin/ai/devchat/plugin/actions/FixIssueIntention.kt +++ b/src/main/kotlin/ai/devchat/plugin/actions/FixIssueIntention.kt @@ -4,7 +4,7 @@ import ai.devchat.common.Constants.ASSISTANT_NAME_EN import ai.devchat.common.Constants.ASSISTANT_NAME_ZH import ai.devchat.core.DevChatActions import ai.devchat.core.handlers.SendUserMessageHandler -import ai.devchat.plugin.DevChatToolWindowFactory +import ai.devchat.plugin.DevChatService import com.alibaba.fastjson.JSONObject import com.intellij.codeInsight.intention.IntentionAction import com.intellij.codeInsight.intention.PriorityAction @@ -29,7 +29,8 @@ class FixIssueIntention : IntentionAction, PriorityAction { val payload = JSONObject(mapOf("message" to "/fix_issue")) ToolWindowManager.getInstance(editor.project!!).getToolWindow(ASSISTANT_NAME_ZH)?.show { - if (DevChatToolWindowFactory.loaded) { + val uiLoaded = editor.project!!.getService(DevChatService::class.java).uiLoaded + if (uiLoaded) { SendUserMessageHandler(project, DevChatActions.SEND_USER_MESSAGE_REQUEST,null, payload).executeAction() } else { SendUserMessageHandler.cache = payload diff --git a/src/main/kotlin/ai/devchat/plugin/hints/ChatCVProviderBase.kt b/src/main/kotlin/ai/devchat/plugin/hints/ChatCVProviderBase.kt index 4671469..d0a226e 100644 --- a/src/main/kotlin/ai/devchat/plugin/hints/ChatCVProviderBase.kt +++ b/src/main/kotlin/ai/devchat/plugin/hints/ChatCVProviderBase.kt @@ -3,7 +3,7 @@ package ai.devchat.plugin.hints import ai.devchat.common.Constants.ASSISTANT_NAME_ZH import ai.devchat.core.DevChatActions import ai.devchat.core.handlers.SendUserMessageHandler -import ai.devchat.plugin.DevChatToolWindowFactory +import ai.devchat.plugin.DevChatService import com.alibaba.fastjson.JSONObject import com.intellij.codeInsight.codeVision.CodeVisionAnchorKind import com.intellij.codeInsight.codeVision.CodeVisionEntry @@ -55,7 +55,8 @@ abstract class ChatCVProviderBase : CodeVisionProviderBase() { val project = editor.project!! ToolWindowManager.getInstance(project).getToolWindow(ASSISTANT_NAME_ZH)?.show { - if (DevChatToolWindowFactory.loaded) { + val uiLoaded = project.getService(DevChatService::class.java).uiLoaded + if (uiLoaded) { SendUserMessageHandler(project, DevChatActions.SEND_USER_MESSAGE_REQUEST,null, payload).executeAction() } else { SendUserMessageHandler.cache = payload diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index cc72413..7bcf066 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -32,9 +32,9 @@ class="ai.devchat.plugin.ToolWindowStateListener" topic="com.intellij.openapi.wm.ex.ToolWindowManagerListener"/> - - - + + + From f00d7afab191195ee01ce533c7091e805955d63e Mon Sep 17 00:00:00 2001 From: Luo Tim Date: Thu, 19 Sep 2024 02:37:37 +0800 Subject: [PATCH 4/4] Make wrapper, conversationManager, browser, ideserver and localService disposable & bind to project lifecycle --- .../kotlin/ai/devchat/common/PathUtils.kt | 1 - .../ai/devchat/core/BaseActionHandler.kt | 14 +++-- .../kotlin/ai/devchat/core/DevChatClient.kt | 3 +- .../kotlin/ai/devchat/core/DevChatWrapper.kt | 8 ++- .../DeleteLastConversationRequestHandler.kt | 2 +- .../handlers/DeleteTopicRequestHandler.kt | 2 +- .../core/handlers/InputRequestHandler.kt | 3 +- .../handlers/ListCommandsRequestHandler.kt | 2 +- .../core/handlers/ListTopicsRequestHandler.kt | 2 +- .../LoadConversationRequestHandler.kt | 9 ++- .../LoadHistoryMessagesRequestHandler.kt | 3 +- .../handlers/SendMessageRequestHandler.kt | 15 +++-- .../handlers/StopGenerationRequestHandler.kt | 3 +- .../devchat/installer/DevChatSetupThread.kt | 32 ++++++++--- src/main/kotlin/ai/devchat/plugin/Browser.kt | 15 +++-- .../plugin/DevChatToolWindowFactory.kt | 56 ++++++------------- .../kotlin/ai/devchat/plugin/IDEServer.kt | 9 ++- .../kotlin/ai/devchat/plugin/LocalService.kt | 10 +++- .../devchat/plugin/ToolWindowStateListener.kt | 2 +- .../ai/devchat/storage/ActiveConversation.kt | 5 +- 20 files changed, 99 insertions(+), 97 deletions(-) diff --git a/src/main/kotlin/ai/devchat/common/PathUtils.kt b/src/main/kotlin/ai/devchat/common/PathUtils.kt index b1aaec9..bc638ac 100644 --- a/src/main/kotlin/ai/devchat/common/PathUtils.kt +++ b/src/main/kotlin/ai/devchat/common/PathUtils.kt @@ -1,6 +1,5 @@ package ai.devchat.common -import ai.devchat.plugin.currentProject import java.io.File import java.io.IOException import java.nio.file.* diff --git a/src/main/kotlin/ai/devchat/core/BaseActionHandler.kt b/src/main/kotlin/ai/devchat/core/BaseActionHandler.kt index 902808c..aff1cbc 100644 --- a/src/main/kotlin/ai/devchat/core/BaseActionHandler.kt +++ b/src/main/kotlin/ai/devchat/core/BaseActionHandler.kt @@ -1,8 +1,9 @@ package ai.devchat.core import ai.devchat.common.Log -import ai.devchat.plugin.DevChatBrowserService +import ai.devchat.plugin.Browser import ai.devchat.plugin.DevChatService +import ai.devchat.storage.ActiveConversation import com.alibaba.fastjson.JSONObject import com.intellij.openapi.project.Project @@ -14,8 +15,11 @@ abstract class BaseActionHandler( var metadata: JSONObject? = null, var payload: JSONObject? = null ) : ActionHandler { - val wrapper = DevChatWrapper(project) - val client: DevChatClient = project.getService(DevChatService::class.java).client!! + private val devChatService: DevChatService = project.getService(DevChatService::class.java) + val client: DevChatClient? = devChatService.client + val wrapper: DevChatWrapper? = devChatService.wrapper + val browser: Browser? = devChatService.browser + val activeConversation: ActiveConversation? = devChatService.activeConversation private val jsCallback: String = metadata?.getString("callback") ?: DEFAULT_RESPONSE_FUNC abstract val actionName: String @@ -39,9 +43,7 @@ abstract class BaseActionHandler( "error" to "" )) response["payload"] = payload ?: JSONObject() - val browser = project.getService(DevChatBrowserService::class.java).browser?.let { - it.executeJS(jsCallback, response) - } + browser!!.executeJS(jsCallback, response) } override fun executeAction() { diff --git a/src/main/kotlin/ai/devchat/core/DevChatClient.kt b/src/main/kotlin/ai/devchat/core/DevChatClient.kt index 3cc3765..f654ef4 100644 --- a/src/main/kotlin/ai/devchat/core/DevChatClient.kt +++ b/src/main/kotlin/ai/devchat/core/DevChatClient.kt @@ -209,7 +209,6 @@ fun timeThis(block: suspend () -> Unit) { } class DevChatClient(val project: Project, private val localServicePort: Int) { - private val scope: CoroutineScope = CoroutineScope(Dispatchers.IO) private val baseURL get() = "http://localhost:$localServicePort" private var job: Job? = null private val workspace: String? = project.basePath @@ -315,7 +314,7 @@ class DevChatClient(val project: Project, private val localServicePort: Int) { onFinish: (Int) -> Unit, ) { cancelMessage() - job = scope.launch { + job = CoroutineScope(Dispatchers.IO).launch { streamPost("/message/msg", message) .catch { e -> onError(e.toString()) diff --git a/src/main/kotlin/ai/devchat/core/DevChatWrapper.kt b/src/main/kotlin/ai/devchat/core/DevChatWrapper.kt index de262e0..861839f 100644 --- a/src/main/kotlin/ai/devchat/core/DevChatWrapper.kt +++ b/src/main/kotlin/ai/devchat/core/DevChatWrapper.kt @@ -6,6 +6,7 @@ import ai.devchat.common.PathUtils import ai.devchat.plugin.ideServerPort import ai.devchat.storage.CONFIG import com.intellij.execution.process.OSProcessUtil.killProcessTree +import com.intellij.openapi.Disposable import com.intellij.openapi.project.Project import com.intellij.util.containers.addIfNotNull import kotlinx.coroutines.* @@ -181,9 +182,10 @@ class Command(private val workDir: String?, val cmd: MutableList = mutab } } -class DevChatWrapper(val project: Project) { +class DevChatWrapper(val project: Project): Disposable { private val apiKey get() = CONFIG["providers.devchat.api_key"] as? String private val defaultModel get() = CONFIG["default_model"] as? String + var activeChannel: SendChannel? = null private val apiBase: String? get() { @@ -242,8 +244,8 @@ class DevChatWrapper(val project: Project) { activeChannel = routeCmd(flags + additionalFlags, callback, onError, onFinish) } - companion object { - var activeChannel: SendChannel? = null + override fun dispose() { + activeChannel?.close() } } diff --git a/src/main/kotlin/ai/devchat/core/handlers/DeleteLastConversationRequestHandler.kt b/src/main/kotlin/ai/devchat/core/handlers/DeleteLastConversationRequestHandler.kt index bf76cb8..d4f1e4a 100644 --- a/src/main/kotlin/ai/devchat/core/handlers/DeleteLastConversationRequestHandler.kt +++ b/src/main/kotlin/ai/devchat/core/handlers/DeleteLastConversationRequestHandler.kt @@ -14,7 +14,7 @@ class DeleteLastConversationRequestHandler(project: Project, requestAction: Stri override val actionName: String = DevChatActions.DELETE_LAST_CONVERSATION_RESPONSE override fun action() { val promptHash = payload!!.getString("promptHash") - client.deleteLog(promptHash) + client!!.deleteLog(promptHash) send(payload = mapOf("promptHash" to promptHash)) } } diff --git a/src/main/kotlin/ai/devchat/core/handlers/DeleteTopicRequestHandler.kt b/src/main/kotlin/ai/devchat/core/handlers/DeleteTopicRequestHandler.kt index 9f89362..659dafb 100644 --- a/src/main/kotlin/ai/devchat/core/handlers/DeleteTopicRequestHandler.kt +++ b/src/main/kotlin/ai/devchat/core/handlers/DeleteTopicRequestHandler.kt @@ -14,7 +14,7 @@ class DeleteTopicRequestHandler(project: Project, requestAction: String, metadat override val actionName: String = DevChatActions.DELETE_TOPIC_RESPONSE override fun action() { val topicHash = payload!!.getString("topicHash") - client.deleteTopic(topicHash) + client!!.deleteTopic(topicHash) send(payload = mapOf("topicHash" to topicHash)) } } diff --git a/src/main/kotlin/ai/devchat/core/handlers/InputRequestHandler.kt b/src/main/kotlin/ai/devchat/core/handlers/InputRequestHandler.kt index 4dafecd..2361943 100644 --- a/src/main/kotlin/ai/devchat/core/handlers/InputRequestHandler.kt +++ b/src/main/kotlin/ai/devchat/core/handlers/InputRequestHandler.kt @@ -2,7 +2,6 @@ package ai.devchat.core.handlers import ai.devchat.core.BaseActionHandler import ai.devchat.core.DevChatActions -import ai.devchat.core.DevChatWrapper import com.alibaba.fastjson.JSONObject import com.intellij.openapi.project.Project import kotlinx.coroutines.runBlocking @@ -17,7 +16,7 @@ class InputRequestHandler(project: Project, requestAction: String, metadata: JSO override fun action() { runBlocking { - DevChatWrapper.activeChannel?.send(payload!!.getString("data")) + wrapper!!.activeChannel?.send(payload!!.getString("data")) } send() } diff --git a/src/main/kotlin/ai/devchat/core/handlers/ListCommandsRequestHandler.kt b/src/main/kotlin/ai/devchat/core/handlers/ListCommandsRequestHandler.kt index 9438678..91fbd06 100644 --- a/src/main/kotlin/ai/devchat/core/handlers/ListCommandsRequestHandler.kt +++ b/src/main/kotlin/ai/devchat/core/handlers/ListCommandsRequestHandler.kt @@ -14,7 +14,7 @@ class ListCommandsRequestHandler(project: Project, requestAction: String, metada ) { override val actionName: String = DevChatActions.LIST_COMMANDS_RESPONSE override fun action() { - val recommendedWorkflows = client.getWorkflowConfig()?.recommend?.workflows.orEmpty() + val recommendedWorkflows = client!!.getWorkflowConfig()?.recommend?.workflows.orEmpty() val indexedCommands = client.getWorkflowList()?.map { val commandName = it.name mapOf( diff --git a/src/main/kotlin/ai/devchat/core/handlers/ListTopicsRequestHandler.kt b/src/main/kotlin/ai/devchat/core/handlers/ListTopicsRequestHandler.kt index 1ba2414..a514de4 100644 --- a/src/main/kotlin/ai/devchat/core/handlers/ListTopicsRequestHandler.kt +++ b/src/main/kotlin/ai/devchat/core/handlers/ListTopicsRequestHandler.kt @@ -14,7 +14,7 @@ class ListTopicsRequestHandler(project: Project, requestAction: String, metadata ) { override val actionName: String = DevChatActions.LIST_TOPICS_RESPONSE override fun action() { - val topics = client.getTopics().map { + val topics = client!!.getTopics().map { val request = it.rootPromptRequest val response = it.rootPromptResponse mapOf( diff --git a/src/main/kotlin/ai/devchat/core/handlers/LoadConversationRequestHandler.kt b/src/main/kotlin/ai/devchat/core/handlers/LoadConversationRequestHandler.kt index de547e0..8eaef50 100644 --- a/src/main/kotlin/ai/devchat/core/handlers/LoadConversationRequestHandler.kt +++ b/src/main/kotlin/ai/devchat/core/handlers/LoadConversationRequestHandler.kt @@ -2,7 +2,6 @@ package ai.devchat.core.handlers import ai.devchat.core.BaseActionHandler import ai.devchat.core.DevChatActions -import ai.devchat.storage.ActiveConversation import com.alibaba.fastjson.JSONObject import com.intellij.openapi.project.Project @@ -18,11 +17,11 @@ class LoadConversationRequestHandler(project: Project, requestAction: String, me val topicHash = metadata!!.getString("topicHash") val res = mutableMapOf("reset" to true) when { - topicHash.isNullOrEmpty() -> ActiveConversation.reset() - topicHash == ActiveConversation.topic -> res["reset"] = false + topicHash.isNullOrEmpty() -> activeConversation!!.reset() + topicHash == activeConversation!!.topic -> res["reset"] = false else -> { - val logs = client.getTopicLogs(topicHash) - ActiveConversation.reset(topicHash, logs) + val logs = client!!.getTopicLogs(topicHash) + activeConversation.reset(topicHash, logs) } } send(payload=res) diff --git a/src/main/kotlin/ai/devchat/core/handlers/LoadHistoryMessagesRequestHandler.kt b/src/main/kotlin/ai/devchat/core/handlers/LoadHistoryMessagesRequestHandler.kt index 445c710..02fc3ca 100644 --- a/src/main/kotlin/ai/devchat/core/handlers/LoadHistoryMessagesRequestHandler.kt +++ b/src/main/kotlin/ai/devchat/core/handlers/LoadHistoryMessagesRequestHandler.kt @@ -2,7 +2,6 @@ package ai.devchat.core.handlers import ai.devchat.core.BaseActionHandler import ai.devchat.core.DevChatActions -import ai.devchat.storage.ActiveConversation import ai.devchat.storage.CONFIG import com.alibaba.fastjson.JSONObject import com.intellij.openapi.project.Project @@ -18,7 +17,7 @@ class LoadHistoryMessagesRequestHandler(project: Project, requestAction: String, override fun action() { val pageSize = CONFIG["max_log_count"] as Int val pageIndex = metadata!!.getInteger("pageIndex") ?: 1 - val messages = ActiveConversation.getMessages(pageIndex, pageSize) + val messages = activeConversation!!.getMessages(pageIndex, pageSize) send(payload = mapOf("messages" to messages)) } } diff --git a/src/main/kotlin/ai/devchat/core/handlers/SendMessageRequestHandler.kt b/src/main/kotlin/ai/devchat/core/handlers/SendMessageRequestHandler.kt index 5c84ab6..f7df73b 100644 --- a/src/main/kotlin/ai/devchat/core/handlers/SendMessageRequestHandler.kt +++ b/src/main/kotlin/ai/devchat/core/handlers/SendMessageRequestHandler.kt @@ -3,7 +3,6 @@ package ai.devchat.core.handlers import ai.devchat.common.Log import ai.devchat.common.PathUtils import ai.devchat.core.* -import ai.devchat.storage.ActiveConversation import ai.devchat.storage.CONFIG import com.alibaba.fastjson.JSONObject import com.intellij.openapi.project.Project @@ -53,7 +52,7 @@ class SendMessageRequestHandler(project: Project, requestAction: String, metadat contextContents = contextContents, ) - client.message( + client!!.message( chatRequest, dataHandler(chatRequest), ::errorHandler, @@ -82,7 +81,7 @@ class SendMessageRequestHandler(project: Project, requestAction: String, metadat addAll(chatRequest.context?.map {"context" to it}.orEmpty()) } - wrapper.route( + wrapper!!.route( flags, chatRequest.content, callback = dataHandler(chatRequest), @@ -103,7 +102,7 @@ class SendMessageRequestHandler(project: Project, requestAction: String, metadat return { exitCode: Int -> when(exitCode) { 0 -> { - val entry = client.insertLog( + val entry = client!!.insertLog( LogEntry( chatRequest.modelName, chatRequest.parent, @@ -115,13 +114,13 @@ class SendMessageRequestHandler(project: Project, requestAction: String, metadat response.promptHash = entry!!.hash promptCallback(response) - val currentTopic = ActiveConversation.topic ?: response.promptHash!! + val currentTopic = activeConversation!!.topic ?: response.promptHash!! val logs = client.getTopicLogs(currentTopic, 0, 1) - if (currentTopic == ActiveConversation.topic) { - ActiveConversation.addMessage(logs.first()) + if (currentTopic == activeConversation.topic) { + activeConversation.addMessage(logs.first()) } else { - ActiveConversation.reset(currentTopic, logs) + activeConversation.reset(currentTopic, logs) } } -1 -> runWorkflow(chatRequest) diff --git a/src/main/kotlin/ai/devchat/core/handlers/StopGenerationRequestHandler.kt b/src/main/kotlin/ai/devchat/core/handlers/StopGenerationRequestHandler.kt index 720c049..711b6b6 100644 --- a/src/main/kotlin/ai/devchat/core/handlers/StopGenerationRequestHandler.kt +++ b/src/main/kotlin/ai/devchat/core/handlers/StopGenerationRequestHandler.kt @@ -2,7 +2,6 @@ package ai.devchat.core.handlers import ai.devchat.core.BaseActionHandler import ai.devchat.core.DevChatActions -import ai.devchat.core.DevChatWrapper import com.alibaba.fastjson.JSONObject import com.intellij.openapi.project.Project @@ -15,7 +14,7 @@ class StopGenerationRequestHandler(project: Project, requestAction: String, meta override val actionName: String = DevChatActions.STOP_GENERATION_RESPONSE override fun action() { - DevChatWrapper.activeChannel?.close() + wrapper!!.activeChannel?.close() send() } } diff --git a/src/main/kotlin/ai/devchat/installer/DevChatSetupThread.kt b/src/main/kotlin/ai/devchat/installer/DevChatSetupThread.kt index 6fadb7a..6dd468e 100644 --- a/src/main/kotlin/ai/devchat/installer/DevChatSetupThread.kt +++ b/src/main/kotlin/ai/devchat/installer/DevChatSetupThread.kt @@ -3,21 +3,23 @@ package ai.devchat.installer import ai.devchat.common.* import ai.devchat.common.Constants.ASSISTANT_NAME_EN import ai.devchat.core.DevChatClient -import ai.devchat.plugin.DevChatBrowserService import ai.devchat.plugin.DevChatService -import ai.devchat.plugin.DevChatToolWindowFactory +import ai.devchat.plugin.LocalService import ai.devchat.storage.CONFIG import ai.devchat.storage.DevChatState import com.intellij.ide.plugins.PluginManagerCore import com.intellij.openapi.extensions.PluginId import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Disposer +import com.intellij.ui.content.Content import java.io.BufferedReader import java.io.File import java.nio.file.Paths -class DevChatSetupThread(val project: Project) : Thread() { +class DevChatSetupThread(val project: Project, val toolWindowContent: Content) : Thread() { private val minimalPythonVersion: String = "3.8" private val defaultPythonVersion: String = "3.11.4" + private val devChatService = project.getService(DevChatService::class.java) private val devChatVersion = PluginManagerCore.getPlugin( PluginId.getId(DevChatBundle.message("plugin.id")) )?.version @@ -29,9 +31,9 @@ class DevChatSetupThread(val project: Project) : Thread() { Log.info("Start configuring the $ASSISTANT_NAME_EN CLI environment.") setup(PythonEnvManager()) DevChatState.instance.lastVersion = devChatVersion - project.getService(DevChatBrowserService::class.java).browser?.let { - it.executeJS("onInitializationFinish") - } + startLocalService() + updateWorkflows() + devChatService.browser?.executeJS("onInitializationFinish") Notifier.info("$ASSISTANT_NAME_EN initialization has completed successfully.") } catch (e: Exception) { Log.error("Failed to install $ASSISTANT_NAME_EN CLI: $e\n" + e.stackTrace.joinToString("\n")) @@ -39,6 +41,18 @@ class DevChatSetupThread(val project: Project) : Thread() { } } + private fun startLocalService() { + try { + val localService = LocalService(project).start() + devChatService.localService = localService + devChatService.client = DevChatClient(project, localService.port!!) + Disposer.register(toolWindowContent, localService) + } catch(e: Exception) { + Log.error("Failed to start local service: ${e.message}") + e.printStackTrace() + } + } + private fun setup(envManager: PythonEnvManager) { val overwrite = devChatVersion != DevChatState.instance.lastVersion PathUtils.copyResourceDirToPath("/tools/site-packages", PathUtils.sitePackagePath, overwrite) @@ -59,7 +73,6 @@ class DevChatSetupThread(val project: Project) : Thread() { ).pythonCommand } } - DevChatToolWindowFactory.pythonReady = true PathUtils.copyResourceDirToPath( "/tools/code-editor/${PathUtils.codeEditorBinary}", Paths.get(PathUtils.toolsPath, PathUtils.codeEditorBinary).toString(), @@ -71,10 +84,11 @@ class DevChatSetupThread(val project: Project) : Thread() { overwrite ) PathUtils.copyResourceDirToPath("/workflows", PathUtils.workflowPath) + } - DevChatToolWindowFactory.pythonReady = true + private fun updateWorkflows() { try { - val dcClient: DevChatClient = project.getService(DevChatService::class.java).client!! + val dcClient: DevChatClient = devChatService.client!! dcClient.updateWorkflows() dcClient.updateCustomWorkflows() } catch (e: Exception) { diff --git a/src/main/kotlin/ai/devchat/plugin/Browser.kt b/src/main/kotlin/ai/devchat/plugin/Browser.kt index 899e57e..39030aa 100644 --- a/src/main/kotlin/ai/devchat/plugin/Browser.kt +++ b/src/main/kotlin/ai/devchat/plugin/Browser.kt @@ -7,8 +7,8 @@ import ai.devchat.core.handlers.AddContextNotifyHandler import ai.devchat.core.handlers.SendUserMessageHandler import ai.devchat.plugin.actions.AddToDevChatAction import com.alibaba.fastjson.JSON +import com.intellij.openapi.Disposable import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.components.Service import com.intellij.openapi.editor.colors.EditorColors import com.intellij.openapi.editor.colors.EditorColorsManager import com.intellij.openapi.project.Project @@ -22,12 +22,7 @@ import org.cef.network.CefRequest import java.awt.Color import java.nio.charset.StandardCharsets -@Service(Service.Level.PROJECT) -class DevChatBrowserService(project: Project) { - var browser: Browser? = null -} - -class Browser(val project: Project) { +class Browser(val project: Project): Disposable { val jbCefBrowser = JBCefBrowserBuilder().setOffScreenRendering(false).setEnableOpenDevToolsMenuItem(true).build() init { @@ -97,10 +92,14 @@ class Browser(val project: Project) { ).executeAction() SendUserMessageHandler.cache = null } - DevChatToolWindowFactory.loaded = true + project.getService(DevChatService::class.java).uiLoaded = true } }, jbCefBrowser.cefBrowser) } + + override fun dispose() { + jbCefBrowser.dispose() + } } class UIBuilder(private val staticResource: String = "/static") { diff --git a/src/main/kotlin/ai/devchat/plugin/DevChatToolWindowFactory.kt b/src/main/kotlin/ai/devchat/plugin/DevChatToolWindowFactory.kt index 461d3a9..434c901 100644 --- a/src/main/kotlin/ai/devchat/plugin/DevChatToolWindowFactory.kt +++ b/src/main/kotlin/ai/devchat/plugin/DevChatToolWindowFactory.kt @@ -4,6 +4,7 @@ import ai.devchat.common.Log import ai.devchat.core.DevChatClient import ai.devchat.core.DevChatWrapper import ai.devchat.installer.DevChatSetupThread +import ai.devchat.storage.ActiveConversation import com.intellij.openapi.Disposable import com.intellij.openapi.components.Service import com.intellij.openapi.project.DumbAware @@ -13,7 +14,6 @@ import com.intellij.openapi.wm.ToolWindow import com.intellij.openapi.wm.ToolWindowFactory import com.intellij.ui.JBColor import com.intellij.ui.jcef.JBCefApp -import kotlinx.coroutines.* import java.awt.BorderLayout import javax.swing.BorderFactory import javax.swing.JLabel @@ -22,64 +22,44 @@ import javax.swing.SwingConstants @Service(Service.Level.PROJECT) class DevChatService(project: Project) { + var activeConversation: ActiveConversation? = null var browser: Browser? = null - var localServicePort: Int? = null + var localService: LocalService? = null var client: DevChatClient? = null + var wrapper: DevChatWrapper? = null + var uiLoaded: Boolean = false } class DevChatToolWindowFactory : ToolWindowFactory, DumbAware, Disposable { - private var ideService: IDEServer? = null - private var localService: LocalService? = null - private var coroutineScope: CoroutineScope? = null - var browser: Browser? = null - override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) { - browser = Browser(project) + val devChatService = project.getService(DevChatService::class.java) + val browser = Browser(project) + devChatService.browser = browser val panel = JPanel(BorderLayout()) if (!JBCefApp.isSupported()) { Log.error("JCEF is not supported.") panel.add(JLabel("JCEF is not supported", SwingConstants.CENTER)) } else { - panel.add(browser!!.jbCefBrowser.component, BorderLayout.CENTER) + panel.add(browser.jbCefBrowser.component, BorderLayout.CENTER) } panel.border = BorderFactory.createMatteBorder(0, 1, 0, 1, JBColor.LIGHT_GRAY) val content = toolWindow.contentManager.factory.createContent(panel, "", false) Disposer.register(content, this) toolWindow.contentManager.addContent(content) - DevChatSetupThread(project).start() - ideService = IDEServer(project).start() - val coroutineExceptionHandler = CoroutineExceptionHandler { _, exception -> - Log.error("Failed to start local service: ${exception.message}") + DevChatSetupThread(project, content).start() + Disposer.register(content, browser) + IDEServer(project).start().let { + Disposer.register(content, it) } - coroutineScope = CoroutineScope(Dispatchers.Default) - coroutineScope!!.launch(coroutineExceptionHandler) { - try { - while (!pythonReady) { - delay(100) - ensureActive() - } - localService = LocalService(project).start() - awaitCancellation() - } finally { - localService?.stop() - } + DevChatWrapper(project).let { + Disposer.register(content, it) + devChatService.wrapper = it } - val devChatService = project.getService(DevChatService::class.java) - devChatService.browser = browser - devChatService.localServicePort = localService?.port - devChatService.client = DevChatClient(project, localService!!.port!!) + devChatService.activeConversation = ActiveConversation() } override fun dispose() { - DevChatWrapper.activeChannel?.close() - coroutineScope?.cancel() - ideService?.stop() - browser?.jbCefBrowser?.dispose() - } - - companion object { - var loaded: Boolean = false - var pythonReady: Boolean = false +// TODO("Not yet implemented") } } diff --git a/src/main/kotlin/ai/devchat/plugin/IDEServer.kt b/src/main/kotlin/ai/devchat/plugin/IDEServer.kt index 5312538..683310a 100644 --- a/src/main/kotlin/ai/devchat/plugin/IDEServer.kt +++ b/src/main/kotlin/ai/devchat/plugin/IDEServer.kt @@ -10,6 +10,7 @@ import com.intellij.codeInsight.intention.IntentionAction import com.intellij.codeInsight.navigation.actions.GotoTypeDeclarationAction import com.intellij.lang.Language import com.intellij.lang.annotation.HighlightSeverity.INFORMATION +import com.intellij.openapi.Disposable import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ReadAction import com.intellij.openapi.editor.Document @@ -78,7 +79,7 @@ data class Result( val result: T? = null ) -class IDEServer(private var project: Project) { +class IDEServer(private var project: Project): Disposable { private var server: ApplicationEngine? = null private var isShutdownHookRegistered: Boolean = false @@ -155,7 +156,7 @@ class IDEServer(private var project: Project) { post("/get_local_service_port") { val devChatService = project.getService(DevChatService::class.java) - call.respond(Result(devChatService.localServicePort)) + call.respond(Result(devChatService.localService?.port)) } post("/ide_language") { @@ -370,6 +371,10 @@ class IDEServer(private var project: Project) { Notifier.info("Stopping IDE server...") server?.stop(1_000, 2_000) } + + override fun dispose() { + stop() + } } fun Editor.range(startOffset: Int, endOffset: Int): Range { diff --git a/src/main/kotlin/ai/devchat/plugin/LocalService.kt b/src/main/kotlin/ai/devchat/plugin/LocalService.kt index 5f081fe..f4e1c09 100644 --- a/src/main/kotlin/ai/devchat/plugin/LocalService.kt +++ b/src/main/kotlin/ai/devchat/plugin/LocalService.kt @@ -9,13 +9,15 @@ import com.intellij.execution.process.OSProcessHandler import com.intellij.execution.process.OSProcessUtil.killProcessTree import com.intellij.execution.process.ProcessAdapter import com.intellij.execution.process.ProcessEvent +import com.intellij.openapi.Disposable +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.project.Project import com.intellij.openapi.util.Key import com.intellij.util.io.BaseOutputReader import java.net.ServerSocket -class LocalService(project: Project) { +class LocalService(project: Project): Disposable { var port: Int? = null val workspace: String? = project.basePath private var processHandler: OSProcessHandler? = null @@ -74,4 +76,10 @@ class LocalService(project: Project) { processHandler = null } ?: Log.info("Local service is not running") } + + override fun dispose() { + ApplicationManager.getApplication().executeOnPooledThread { + stop() + } + } } \ No newline at end of file diff --git a/src/main/kotlin/ai/devchat/plugin/ToolWindowStateListener.kt b/src/main/kotlin/ai/devchat/plugin/ToolWindowStateListener.kt index 760de8b..2433670 100644 --- a/src/main/kotlin/ai/devchat/plugin/ToolWindowStateListener.kt +++ b/src/main/kotlin/ai/devchat/plugin/ToolWindowStateListener.kt @@ -36,7 +36,7 @@ class ToolWindowStateListener : ToolWindowManagerListener { ToolWindowManagerListener.ToolWindowManagerEventType.ActivateToolWindow -> { val jsFocus = "document.getElementsByClassName('mantine-Input-input mantine-Textarea-input')[0].focus();" toolWindowManager.getToolWindow(DevChatBundle.message("plugin.id"))?.project?.let { project -> - project.getService(DevChatBrowserService::class.java).browser?.jbCefBrowser?.cefBrowser?.executeJavaScript(jsFocus, "", 0) + project.getService(DevChatService::class.java).browser?.jbCefBrowser?.cefBrowser?.executeJavaScript(jsFocus, "", 0) } DevChatState.instance.lastToolWindowState = ToolWindowState.SHOWN.name } diff --git a/src/main/kotlin/ai/devchat/storage/ActiveConversation.kt b/src/main/kotlin/ai/devchat/storage/ActiveConversation.kt index b85a29b..c5c0b87 100644 --- a/src/main/kotlin/ai/devchat/storage/ActiveConversation.kt +++ b/src/main/kotlin/ai/devchat/storage/ActiveConversation.kt @@ -3,7 +3,7 @@ package ai.devchat.storage import ai.devchat.core.ShortLog -object ActiveConversation { +class ActiveConversation { private var messages: MutableList? = null var topic: String? = null @@ -46,5 +46,4 @@ object ActiveConversation { offset..