Skip to content

Commit 5f3c0dc

Browse files
committed
feat(shire): implement chat completion task and editor interaction for code suggestions #379
1 parent a3de41e commit 5f3c0dc

21 files changed

+897
-61
lines changed

core/src/main/kotlin/cc/unitmesh/devti/gui/AutoDevToolWindowFactory.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ class AutoDevToolWindowFactory : ToolWindowFactory, DumbAware {
7171
}
7272

7373
companion object {
74-
7574
fun labelNormalChat(
7675
toolWindowManager: ToolWindow,
7776
chatCodingService: ChatCodingService

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import cc.unitmesh.devti.gui.toolbar.NewChatAction
1818
import cc.unitmesh.devti.provider.TextContextPrompter
1919
import cc.unitmesh.devti.provider.devins.LanguageProcessor
2020
import cc.unitmesh.devti.settings.AutoDevSettingsState
21-
import cc.unitmesh.devti.settings.locale.LanguageChangedCallback.componentStateChanged
2221
import cc.unitmesh.devti.sketch.createActionButton
2322
import cc.unitmesh.devti.sketch.ui.code.HtmlHighlightSketch
2423
import cc.unitmesh.devti.util.whenDisposed

core/src/main/resources/messages/AutoDevBundle_en.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,3 +270,4 @@ shire.toolchain.function.not.found=Toolchain Not Found
270270
shire.run.local.mode=Run Local Model
271271
devins.llm.notfound=LLM not found
272272
devins.llm.done=Done
273+
intentions.step.prepare-context=Prepare Context

core/src/main/resources/messages/AutoDevBundle_zh.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,3 +260,4 @@ shire.toolchain.function.not.found=Toolchain Not Found
260260
shire.run.local.mode=Run Local Model
261261
devins.llm.notfound=LLM not found
262262
devins.llm.done=Done
263+
intentions.step.prepare-context=Prepare Context

exts/devins-lang/src/main/kotlin/cc/unitmesh/devti/language/actions/DevInsRunFileAction.kt

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import cc.unitmesh.devti.language.psi.DevInFile
66
import cc.unitmesh.devti.language.run.DevInsConfiguration
77
import cc.unitmesh.devti.language.run.DevInsConfigurationType
88
import cc.unitmesh.devti.language.run.DevInsRunConfigurationProducer
9+
import cc.unitmesh.devti.language.run.runner.ShireConsoleView
910
import cc.unitmesh.devti.language.status.DevInsRunListener
1011
import com.intellij.execution.ExecutionManager
1112
import com.intellij.execution.RunManager
@@ -105,23 +106,16 @@ class DevInsRunFileAction : DumbAwareAction() {
105106
val hintDisposable = Disposer.newDisposable()
106107
val connection = ApplicationManager.getApplication().messageBus.connect(hintDisposable)
107108
connection.subscribe(DevInsRunListener.TOPIC, object : DevInsRunListener {
108-
// override fun runFinish(
109-
// allOutput: String,
110-
// llmOutput: String,
111-
// event: ProcessEvent,
112-
// scriptPath: String,
113-
// consoleView: DevInConsoleView?,
114-
// ) {
115-
// future.complete(llmOutput)
116-
// connection.disconnect()
117-
// Disposer.dispose(hintDisposable)
118-
// }
119109
override fun runFinish(
120-
string: String,
110+
allOutput: String,
111+
llmOutput: String,
121112
event: ProcessEvent,
122-
scriptPath: String
113+
scriptPath: String,
114+
consoleView: ShireConsoleView?,
123115
) {
124-
116+
future.complete(llmOutput)
117+
connection.disconnect()
118+
Disposer.dispose(hintDisposable)
125119
}
126120
})
127121

exts/devins-lang/src/main/kotlin/cc/unitmesh/devti/language/compiler/service/DevInRunService.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package cc.unitmesh.devti.language.compiler.service
22

33
import cc.unitmesh.devti.language.run.DevInsConfiguration
4+
import cc.unitmesh.devti.language.run.runner.ShireConsoleView
45
import cc.unitmesh.devti.language.status.DevInsRunListener
56
import cc.unitmesh.devti.provider.RunService
67
import com.intellij.execution.ExecutionManager
@@ -63,8 +64,10 @@ class DevInRunService : RunService {
6364
connection.subscribe(DevInsRunListener.TOPIC, object : DevInsRunListener {
6465
override fun runFinish(
6566
string: String,
67+
llmOutput: String,
6668
event: ProcessEvent,
67-
scriptPath: String
69+
scriptPath: String,
70+
consoleView: ShireConsoleView?
6871
) {
6972
future.complete(string)
7073
/// append to input box
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package cc.unitmesh.devti.language.config
2+
3+
import cc.unitmesh.devti.AutoDevBundle
4+
import cc.unitmesh.devti.llms.LlmFactory
5+
import cc.unitmesh.devti.llms.cancelHandler
6+
import cc.unitmesh.devti.util.AutoDevCoroutineScope
7+
import cc.unitmesh.devti.util.parser.CodeFence
8+
import com.intellij.openapi.util.TextRange
9+
10+
import com.intellij.openapi.actionSystem.CustomShortcutSet
11+
import com.intellij.openapi.actionSystem.KeyboardShortcut
12+
import com.intellij.openapi.application.invokeLater
13+
import com.intellij.openapi.diagnostic.logger
14+
import com.intellij.openapi.progress.ProgressIndicator
15+
import com.intellij.openapi.project.DumbAwareAction
16+
import kotlinx.coroutines.cancel
17+
import kotlinx.coroutines.flow.*
18+
import kotlinx.coroutines.launch
19+
import java.awt.event.KeyEvent
20+
import javax.swing.KeyStroke
21+
22+
open class ChatCompletionTask(private val request: ShireCodeCompletionRequest) :
23+
ShireInteractionTask(request.project, AutoDevBundle.message("intentions.chat.code.complete.name"), request.postExecute) {
24+
private val logger = logger<ChatCompletionTask>()
25+
26+
private var isCanceled: Boolean = false
27+
28+
private var cancelCallback: ((String) -> Unit)? = null
29+
30+
override fun run(indicator: ProgressIndicator) {
31+
indicator.isIndeterminate = false
32+
indicator.fraction = 0.1
33+
indicator.text = AutoDevBundle.message("intentions.step.prepare-context")
34+
35+
val flow: Flow<String> = LlmFactory.create(request.project)!!.stream(request.userPrompt, "", false)
36+
logger.info("Prompt: ${request.userPrompt}")
37+
38+
val shortcutAction = DumbAwareAction.create {
39+
isCanceled = true
40+
}.apply {
41+
registerCustomShortcutSet(
42+
CustomShortcutSet(
43+
KeyboardShortcut(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), null),
44+
),
45+
request.editor.component
46+
)
47+
}
48+
49+
val editor = request.editor
50+
val project = request.project
51+
52+
var currentOffset = request.startOffset
53+
val modifyStart = request.startOffset
54+
55+
indicator.isIndeterminate = false
56+
indicator.fraction = 0.5
57+
indicator.text = AutoDevBundle.message("intentions.request.background.process.title")
58+
59+
AutoDevCoroutineScope.scope(request.project).launch {
60+
val suggestion = StringBuilder()
61+
62+
flow.cancelHandler { cancelCallback = it }.cancellable().collect { char ->
63+
if (isCanceled) {
64+
cancel()
65+
return@collect
66+
}
67+
68+
suggestion.append(char)
69+
70+
invokeLater {
71+
if (!isCanceled && !request.isReplacement) {
72+
if (request.isInsertBefore) {
73+
InsertUtil.insertStreamingToDoc(project, char, editor, currentOffset)
74+
currentOffset += char.length
75+
} else {
76+
InsertUtil.insertStreamingToDoc(project, char, editor, currentOffset)
77+
currentOffset += char.length
78+
}
79+
}
80+
}
81+
}
82+
83+
val modifyEnd = currentOffset
84+
85+
if (request.isReplacement) {
86+
val parsedContent = CodeFence.parse(suggestion.toString()).text
87+
InsertUtil.replaceText(project, editor, parsedContent)
88+
}
89+
90+
indicator.fraction = 0.8
91+
logger.info("Suggestion: $suggestion")
92+
93+
val textRange = TextRange(modifyStart, modifyEnd)
94+
95+
request.postExecute.invoke(suggestion.toString(), textRange)
96+
indicator.fraction = 1.0
97+
}.invokeOnCompletion {
98+
shortcutAction.unregisterCustomShortcutSet(editor.component)
99+
}
100+
}
101+
102+
override fun onThrowable(error: Throwable) {
103+
super.onThrowable(error)
104+
}
105+
106+
override fun onCancel() {
107+
this.isCanceled = true
108+
this.cancelCallback?.invoke("This job is canceled")
109+
super.onCancel()
110+
}
111+
}

0 commit comments

Comments
 (0)