Skip to content

Commit 6cdd753

Browse files
authored
Support real-time code formatting when LLM return results #252 (#254)
1 parent 11105dd commit 6cdd753

File tree

5 files changed

+107
-33
lines changed

5 files changed

+107
-33
lines changed

core/src/main/kotlin/cc/unitmesh/devti/gui/chat/ChatCodingPanel.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,6 @@ class ChatCodingPanel(private val chatCodingService: ChatCodingService, val disp
260260
}
261261
}
262262

263-
messageView.reRenderAssistantOutput()
264263
return text
265264
}
266265

core/src/main/kotlin/cc/unitmesh/devti/gui/chat/MessageView.kt

Lines changed: 55 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package cc.unitmesh.devti.gui.chat
22

33

4-
import cc.unitmesh.devti.gui.component.DisplayComponent
54
import com.intellij.openapi.actionSystem.DataProvider
65
import com.intellij.openapi.actionSystem.ex.ActionUtil
76
import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl
@@ -23,9 +22,9 @@ import kotlin.jvm.internal.Ref
2322
class MessageView(val message: String, val role: ChatRole, private val displayText: String) :
2423
JBPanel<MessageView>(), DataProvider {
2524
private val myNameLabel: Component
26-
private val component: DisplayComponent = DisplayComponent(message)
2725
private var centerPanel: JPanel = JPanel(VerticalLayout(JBUI.scale(8)))
2826
private var messageView: SimpleMessage? = null
27+
private var blocks: MutableList<MessageBlock> = ArrayList()
2928

3029
init {
3130
isDoubleBuffered = true
@@ -55,17 +54,10 @@ class MessageView(val message: String, val role: ChatRole, private val displayTe
5554
centerPanel.add(myNameLabel)
5655
add(centerPanel, BorderLayout.CENTER)
5756

58-
if (role == ChatRole.User) {
59-
ApplicationManager.getApplication().invokeLater {
60-
val simpleMessage = SimpleMessage(displayText, message, role)
61-
messageView = simpleMessage
62-
renderInPartView(simpleMessage)
63-
}
64-
} else {
65-
component.updateMessage(message)
66-
component.revalidate()
67-
component.repaint()
68-
centerPanel.add(component)
57+
ApplicationManager.getApplication().invokeLater {
58+
val simpleMessage = SimpleMessage(displayText, message, role)
59+
messageView = simpleMessage
60+
renderInPartView(simpleMessage)
6961
}
7062
}
7163

@@ -89,25 +81,58 @@ class MessageView(val message: String, val role: ChatRole, private val displayTe
8981
}
9082

9183
private fun renderInPartView(message: SimpleMessage) {
92-
val parts = layoutAll(message)
93-
parts.forEach {
94-
val blockView = when (it) {
95-
is CodeBlock -> {
96-
val project = ProjectManager.getInstance().openProjects.firstOrNull()
97-
CodeBlockView(it, project!!) { }
84+
val incrBlocks = layoutAll(message)
85+
if (incrBlocks.isEmpty()) {
86+
return
87+
}
88+
if (this.blocks.isEmpty()) {
89+
blocks.addAll(incrBlocks)
90+
} else {
91+
var newBlock: MessageBlock
92+
var i = 0
93+
while (i < minOf(this.blocks.size, incrBlocks.size)) {
94+
val oldBlock = this.blocks[i]
95+
newBlock = incrBlocks[i]
96+
if (oldBlock.getIdentifier() == newBlock.getIdentifier()) { // 如果新旧内容一样忽略
97+
i++
98+
continue
9899
}
99-
100-
else -> TextBlockView(it)
100+
oldBlock.setNeedUpdate(true)
101+
oldBlock.addContent(newBlock.getTextContent())
102+
i++
101103
}
104+
for (j in i until incrBlocks.size) {
105+
newBlock = incrBlocks[j]
106+
newBlock.setNeedUpdate(true)
107+
this.blocks.add(newBlock) // 添加到 blocks
108+
}
109+
}
110+
renderComponent();
111+
}
102112

103-
blockView.initialize()
104-
val component = blockView.getComponent() ?: return@forEach
113+
private fun renderComponent(){
114+
blocks.forEach { block ->
115+
if (!block.isNeedUpdate()) return@forEach
105116

106-
component.setForeground(JBUI.CurrentTheme.Label.foreground())
107-
centerPanel.add(component)
117+
if (block.getMessageBlockView() == null) {
118+
val blockView = when (block) {
119+
is CodeBlock -> {
120+
val project = ProjectManager.getInstance().openProjects.first()
121+
CodeBlockView(block, project) { }
122+
}
123+
else -> TextBlockView(block)
124+
}
125+
blockView.initialize()
126+
block.setMessageBlockView(blockView)
127+
128+
val component = blockView.getComponent() ?: return@forEach
129+
component.setForeground(JBUI.CurrentTheme.Label.foreground())
130+
centerPanel.add(component)
131+
}
132+
block.setNeedUpdate(false)
108133
}
109-
}
110134

135+
}
111136
private var answer: String = ""
112137

113138
fun updateContent(content: String) {
@@ -124,7 +149,7 @@ class MessageView(val message: String, val role: ChatRole, private val displayTe
124149

125150
fun reRenderAssistantOutput() {
126151
ApplicationManager.getApplication().invokeLater {
127-
centerPanel.remove(component)
152+
128153
centerPanel.updateUI()
129154

130155
centerPanel.add(myNameLabel)
@@ -148,8 +173,8 @@ class MessageView(val message: String, val role: ChatRole, private val displayTe
148173
override fun done() {
149174
try {
150175
get()
151-
component.updateMessage(message)
152-
component.updateUI()
176+
val simpleMessage = SimpleMessage(message, this@MessageView.message, role)
177+
renderInPartView(simpleMessage)
153178
} catch (e: Exception) {
154179
logger.error(message, e.message)
155180
}
@@ -246,7 +271,7 @@ class MessageView(val message: String, val role: ChatRole, private val displayTe
246271

247272
override fun getData(dataId: String): CompletableMessage? {
248273
return if (CompletableMessage.key.`is`(dataId)) {
249-
return this.messageView
274+
return this.messageView
250275
} else {
251276
null
252277
}

core/src/main/kotlin/cc/unitmesh/devti/llms/azure/AzureOpenAIProvider.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,4 +199,4 @@ class AzureOpenAIProvider(val project: Project) : LLMProvider {
199199
return customOpenAiHost
200200
}
201201
}
202-
}
202+
}

core/src/main/kotlin/com/intellij/temporary/gui/block/CodeBlockView.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import com.intellij.openapi.Disposable
88
import com.intellij.openapi.actionSystem.ActionGroup
99
import com.intellij.openapi.actionSystem.ActionManager
1010
import com.intellij.openapi.actionSystem.ActionPlaces
11+
import com.intellij.openapi.application.ApplicationManager
1112
import com.intellij.openapi.application.ReadAction
1213
import com.intellij.openapi.editor.Document
1314
import com.intellij.openapi.editor.Editor
@@ -80,7 +81,9 @@ class CodeBlockView(
8081
if (codePartEditorInfo!!.language == code.language) {
8182
editorInfo!!.language = code.language
8283
}
83-
84+
ApplicationManager.getApplication().runWriteAction{
85+
editorInfo!!.editor.document.setText(code.text)
86+
}
8487
editorInfo!!.code.set(code.text)
8588
}
8689

core/src/main/kotlin/com/intellij/temporary/gui/block/MessageBlock.kt

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,19 @@ package com.intellij.temporary.gui.block
33

44
import cc.unitmesh.devti.util.parser.CodeFence
55
import com.intellij.lang.Language
6+
import java.math.BigInteger
7+
import java.security.MessageDigest
8+
import java.security.NoSuchAlgorithmException
69

710
interface MessageBlock {
811
val type: MessageBlockType
912
fun getTextContent(): String
1013
fun getMessage(): CompletableMessage
14+
fun setNeedUpdate(needUpdate: Boolean)
15+
fun isNeedUpdate(): Boolean
16+
fun getIdentifier(): String
17+
fun getMessageBlockView(): MessageBlockView?
18+
fun setMessageBlockView(messageBlockView: MessageBlockView)
1119
fun addContent(addedContent: String)
1220
fun replaceContent(content: String)
1321
fun addTextListener(textListener: MessageBlockTextListener)
@@ -16,19 +24,58 @@ interface MessageBlock {
1624

1725
abstract class AbstractMessageBlock(open val completableMessage: CompletableMessage) : MessageBlock {
1826
private val contentBuilder: StringBuilder = StringBuilder()
27+
private var identifier: String = ""
28+
private var needUpdate: Boolean = true
29+
private var messageBlockView: MessageBlockView? = null
1930
private val textListeners: MutableList<MessageBlockTextListener> = mutableListOf()
2031

32+
companion object {
33+
fun encode(bytes: ByteArray): String {
34+
var md5 = ""
35+
try {
36+
val messageDigest = MessageDigest.getInstance("MD5")
37+
messageDigest.update(bytes)
38+
md5 = BigInteger(1, messageDigest.digest()).toString(16)
39+
} catch (noSuchAlgorithmException: NoSuchAlgorithmException) {
40+
// empty catch block
41+
}
42+
return md5
43+
}
44+
}
45+
override fun isNeedUpdate(): Boolean {
46+
return needUpdate
47+
}
48+
49+
override fun setNeedUpdate(needUpdate: Boolean) {
50+
this.needUpdate = needUpdate
51+
}
52+
53+
override fun getMessageBlockView(): MessageBlockView? {
54+
return messageBlockView;
55+
}
56+
57+
override fun setMessageBlockView(messageBlockView: MessageBlockView) {
58+
this.messageBlockView = messageBlockView;
59+
}
60+
61+
override fun getIdentifier(): String {
62+
return identifier;
63+
}
64+
2165
override fun addContent(addedContent: String) {
66+
contentBuilder.clear()
2267
contentBuilder.append(addedContent)
2368
onContentAdded(addedContent)
2469
val content = contentBuilder.toString()
70+
identifier = encode(content.toByteArray())
2571
onContentChanged(content)
2672
fireTextChanged(content)
2773
}
2874

2975
override fun replaceContent(content: String) {
3076
contentBuilder.clear()
3177
contentBuilder.append(content)
78+
identifier = encode(content.toByteArray())
3279
onContentChanged(content)
3380
fireTextChanged(content)
3481
}

0 commit comments

Comments
 (0)