Skip to content

Commit 160e8fa

Browse files
authored
Merge pull request #23 from phodal/feature/idea-devin-editor-input
feat(mpp-idea): migrate DevInEditorInput functionality to mpp-idea module
2 parents 92a5b3c + fffab93 commit 160e8fa

File tree

14 files changed

+1385
-411
lines changed

14 files changed

+1385
-411
lines changed

mpp-idea/build.gradle.kts

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ plugins {
55
kotlin("jvm")
66
id("org.jetbrains.intellij.platform")
77
kotlin("plugin.compose")
8-
kotlin("plugin.serialization")
98
}
109

1110
group = "cc.unitmesh.devins"
@@ -102,15 +101,40 @@ dependencies {
102101
}
103102

104103
// Use platform-provided kotlinx libraries to avoid classloader conflicts
105-
compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
106104
compileOnly("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1")
107105

106+
// Gson for JSON serialization (used by IdeaRemoteAgentClient)
107+
compileOnly("com.google.code.gson:gson:2.11.0")
108+
108109
// Note: We use SimpleJewelMarkdown with intellij-markdown parser instead of mikepenz
109110
// to avoid Compose runtime version mismatch with IntelliJ's bundled Compose
110111

111112
// SQLite JDBC driver for SQLDelight (required at runtime)
112113
implementation("org.xerial:sqlite-jdbc:3.49.1.0")
113114

115+
// DevIn language support for @ and / completion
116+
// These provide the DevIn language parser, completion contributors, and core functionality
117+
implementation("AutoDev-Intellij:exts-devins-lang:$mppVersion") {
118+
// Exclude kotlinx libraries - IntelliJ provides its own
119+
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-core")
120+
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-core-jvm")
121+
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-swing")
122+
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-serialization-json")
123+
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-serialization-json-jvm")
124+
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-serialization-core")
125+
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-serialization-core-jvm")
126+
}
127+
implementation("AutoDev-Intellij:core:$mppVersion") {
128+
// Exclude kotlinx libraries - IntelliJ provides its own
129+
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-core")
130+
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-core-jvm")
131+
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-swing")
132+
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-serialization-json")
133+
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-serialization-json-jvm")
134+
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-serialization-core")
135+
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-serialization-core-jvm")
136+
}
137+
114138
// Ktor HTTP Client for LLM API calls - use compileOnly for libraries that may conflict
115139
compileOnly("io.ktor:ktor-client-core:3.2.2")
116140
compileOnly("io.ktor:ktor-client-cio:3.2.2")
@@ -128,7 +152,7 @@ dependencies {
128152
// Target IntelliJ IDEA 2025.2+ for Compose support
129153
create("IC", "2025.2.1")
130154

131-
bundledPlugins("com.intellij.java")
155+
bundledPlugins("com.intellij.java", "org.intellij.plugins.markdown", "com.jetbrains.sh", "Git4Idea")
132156

133157
// Compose support dependencies (bundled in IDEA 252+)
134158
bundledModules(

mpp-idea/gradle.properties

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Gradle JVM memory settings
2+
org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1g -Dfile.encoding=UTF-8
3+
4+
# Kotlin daemon memory settings
5+
kotlin.daemon.jvmargs=-Xmx4g
6+
7+
# Enable Gradle Configuration Cache
8+
org.gradle.configuration-cache = true
9+
10+
# Enable Gradle Build Cache
11+
org.gradle.caching = true
12+
13+
# Kotlin stdlib
14+
kotlin.stdlib.default.dependency = false
15+

mpp-idea/settings.gradle.kts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@ pluginManagement {
1010
}
1111

1212
plugins {
13-
kotlin("jvm") version "2.1.20"
14-
kotlin("plugin.compose") version "2.1.20"
15-
kotlin("plugin.serialization") version "2.1.20"
13+
kotlin("jvm") version "2.2.0"
14+
kotlin("plugin.compose") version "2.2.0"
1615
id("org.jetbrains.intellij.platform") version "2.10.2"
1716
}
1817
}
@@ -24,13 +23,17 @@ pluginManagement {
2423
// - mpp-core: group = "cc.unitmesh"
2524
// - mpp-codegraph: uses root project name
2625
// - mpp-viewer: group = "cc.unitmesh.viewer"
26+
// - devins-lang, core: uses root project name "AutoDev-Intellij" as group
2727
includeBuild("..") {
2828
dependencySubstitution {
2929
// Substitute Maven coordinates with project dependencies
3030
substitute(module("AutoDev-Intellij:mpp-ui")).using(project(":mpp-ui")).because("Using local project")
3131
substitute(module("cc.unitmesh:mpp-core")).using(project(":mpp-core")).because("Using local project")
3232
substitute(module("AutoDev-Intellij:mpp-codegraph")).using(project(":mpp-codegraph")).because("Using local project")
3333
substitute(module("cc.unitmesh.viewer:mpp-viewer")).using(project(":mpp-viewer")).because("Using local project")
34+
// DevIn language support for @ and / completion
35+
substitute(module("AutoDev-Intellij:exts-devins-lang")).using(project(":exts:devins-lang")).because("Using local project")
36+
substitute(module("AutoDev-Intellij:core")).using(project(":core")).because("Using local project")
3437
}
3538
}
3639

mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaBottomToolbar.kt

Lines changed: 33 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,22 @@ import org.jetbrains.jewel.ui.component.Icon
1919

2020
/**
2121
* Bottom toolbar for the input section.
22-
* Provides send/stop buttons, @ trigger for agent completion, / command trigger, model selector, settings, and token info.
22+
* Provides send/stop buttons, model selector, settings, and token info.
2323
*
24-
* Layout: Workspace - Token Info - ModelSelector - @ Symbol - / Symbol - Settings - Send Button
24+
* Layout: ModelSelector - Token Info | MCP Settings - Prompt Optimization - Send Button
25+
* - Left side: Model configuration (blends with background)
26+
* - Right side: MCP, prompt optimization, and send
2527
*
26-
* Uses Jewel components for native IntelliJ IDEA look and feel.
28+
* Note: @ and / triggers are now in the top toolbar (IdeaTopToolbar).
2729
*/
2830
@Composable
2931
fun IdeaBottomToolbar(
3032
onSendClick: () -> Unit,
3133
sendEnabled: Boolean,
3234
isExecuting: Boolean = false,
3335
onStopClick: () -> Unit = {},
34-
onAtClick: () -> Unit = {},
35-
onSlashClick: () -> Unit = {},
3636
onSettingsClick: () -> Unit = {},
37-
workspacePath: String? = null,
37+
onPromptOptimizationClick: () -> Unit = {},
3838
totalTokens: Int? = null,
3939
// Model selector props
4040
availableConfigs: List<NamedModelConfig> = emptyList(),
@@ -46,74 +46,33 @@ fun IdeaBottomToolbar(
4646
Row(
4747
modifier = modifier
4848
.fillMaxWidth()
49-
.padding(horizontal = 8.dp, vertical = 6.dp),
49+
.padding(horizontal = 4.dp, vertical = 4.dp),
5050
horizontalArrangement = Arrangement.SpaceBetween,
5151
verticalAlignment = Alignment.CenterVertically
5252
) {
53-
// Left side: workspace and token info
53+
// Left side: Model selector and token info
5454
Row(
55-
horizontalArrangement = Arrangement.spacedBy(8.dp),
55+
horizontalArrangement = Arrangement.spacedBy(4.dp),
5656
verticalAlignment = Alignment.CenterVertically,
5757
modifier = Modifier.weight(1f, fill = false)
5858
) {
59-
// Workspace indicator
60-
if (!workspacePath.isNullOrEmpty()) {
61-
// Extract project name from path, handling both Unix and Windows separators
62-
val projectName = workspacePath
63-
.replace('\\', '/') // Normalize to Unix separator
64-
.substringAfterLast('/')
65-
.ifEmpty { "Project" }
66-
67-
Box(
68-
modifier = Modifier
69-
.clip(RoundedCornerShape(4.dp))
70-
.background(JewelTheme.globalColors.panelBackground.copy(alpha = 0.8f))
71-
.padding(horizontal = 8.dp, vertical = 4.dp)
72-
) {
73-
Row(
74-
verticalAlignment = Alignment.CenterVertically,
75-
horizontalArrangement = Arrangement.spacedBy(4.dp)
76-
) {
77-
Icon(
78-
imageVector = IdeaComposeIcons.Folder,
79-
contentDescription = null,
80-
tint = JewelTheme.globalColors.text.normal,
81-
modifier = Modifier.size(12.dp)
82-
)
83-
Text(
84-
text = projectName,
85-
style = JewelTheme.defaultTextStyle.copy(fontSize = 12.sp),
86-
maxLines = 1
87-
)
88-
}
89-
}
90-
}
59+
// Model selector (transparent, blends with background)
60+
IdeaModelSelector(
61+
availableConfigs = availableConfigs,
62+
currentConfigName = currentConfigName,
63+
onConfigSelect = onConfigSelect,
64+
onConfigureClick = onConfigureClick
65+
)
9166

92-
// Token usage indicator
67+
// Token usage indicator (subtle)
9368
if (totalTokens != null && totalTokens > 0) {
94-
Box(
95-
modifier = Modifier
96-
.clip(RoundedCornerShape(4.dp))
97-
.background(AutoDevColors.Blue.c400.copy(alpha = 0.2f))
98-
.padding(horizontal = 8.dp, vertical = 4.dp)
99-
) {
100-
Row(
101-
verticalAlignment = Alignment.CenterVertically,
102-
horizontalArrangement = Arrangement.spacedBy(4.dp)
103-
) {
104-
Text(
105-
text = "Token",
106-
style = JewelTheme.defaultTextStyle.copy(fontSize = 11.sp)
107-
)
108-
Text(
109-
text = "$totalTokens",
110-
style = JewelTheme.defaultTextStyle.copy(
111-
fontSize = 11.sp,
112-
fontWeight = FontWeight.Bold
113-
)
114-
)
115-
}
116-
}
69+
Text(
70+
text = "${totalTokens}t",
71+
style = JewelTheme.defaultTextStyle.copy(
72+
fontSize = 11.sp,
73+
color = JewelTheme.globalColors.text.normal.copy(alpha = 0.6f)
74+
)
75+
)
11776
}
11877
}
11978

@@ -122,49 +81,27 @@ fun IdeaBottomToolbar(
12281
horizontalArrangement = Arrangement.spacedBy(4.dp),
12382
verticalAlignment = Alignment.CenterVertically
12483
) {
125-
// Model selector
126-
IdeaModelSelector(
127-
availableConfigs = availableConfigs,
128-
currentConfigName = currentConfigName,
129-
onConfigSelect = onConfigSelect,
130-
onConfigureClick = onConfigureClick
131-
)
132-
133-
// @ trigger button for agent completion
84+
// MCP Settings button
13485
IconButton(
135-
onClick = onAtClick,
86+
onClick = onSettingsClick,
13687
modifier = Modifier.size(32.dp)
13788
) {
13889
Icon(
139-
imageVector = IdeaComposeIcons.AlternateEmail,
140-
contentDescription = "@ Agent",
90+
imageVector = IdeaComposeIcons.Settings,
91+
contentDescription = "MCP Settings",
14192
tint = JewelTheme.globalColors.text.normal,
142-
modifier = Modifier.size(18.dp)
143-
)
144-
}
145-
146-
// / trigger button for slash commands
147-
IconButton(
148-
onClick = onSlashClick,
149-
modifier = Modifier.size(32.dp)
150-
) {
151-
Text(
152-
text = "/",
153-
style = JewelTheme.defaultTextStyle.copy(
154-
fontSize = 16.sp,
155-
fontWeight = FontWeight.Bold
156-
)
93+
modifier = Modifier.size(16.dp)
15794
)
15895
}
15996

160-
// Settings button
97+
// Prompt Optimization button
16198
IconButton(
162-
onClick = onSettingsClick,
99+
onClick = onPromptOptimizationClick,
163100
modifier = Modifier.size(32.dp)
164101
) {
165102
Icon(
166-
imageVector = IdeaComposeIcons.Settings,
167-
contentDescription = "Settings",
103+
imageVector = IdeaComposeIcons.AutoAwesome,
104+
contentDescription = "Prompt Optimization",
168105
tint = JewelTheme.globalColors.text.normal,
169106
modifier = Modifier.size(16.dp)
170107
)

mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaDevInInput.kt

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
package cc.unitmesh.devins.idea.editor
22

3+
import cc.unitmesh.devti.language.DevInLanguage
4+
import cc.unitmesh.devti.util.InsertUtil
5+
import com.intellij.codeInsight.AutoPopupController
36
import com.intellij.codeInsight.lookup.LookupManagerListener
4-
import com.intellij.lang.Language
57
import com.intellij.openapi.Disposable
68
import com.intellij.openapi.actionSystem.CustomShortcutSet
79
import com.intellij.openapi.actionSystem.KeyboardShortcut
810
import com.intellij.openapi.command.CommandProcessor
911
import com.intellij.openapi.command.WriteCommandAction
1012
import com.intellij.openapi.editor.Document
1113
import com.intellij.openapi.editor.Editor
12-
import com.intellij.openapi.editor.EditorFactory
1314
import com.intellij.openapi.editor.EditorModificationUtil
1415
import com.intellij.openapi.application.ReadAction
1516
import com.intellij.openapi.application.runReadAction
@@ -21,6 +22,8 @@ import com.intellij.openapi.fileTypes.FileTypes
2122
import com.intellij.openapi.project.DumbAwareAction
2223
import com.intellij.openapi.project.Project
2324
import com.intellij.openapi.util.TextRange
25+
import com.intellij.psi.PsiDocumentManager
26+
import com.intellij.psi.PsiFileFactory
2427
import com.intellij.testFramework.LightVirtualFile
2528
import com.intellij.ui.EditorTextField
2629
import com.intellij.util.EventDispatcher
@@ -32,17 +35,18 @@ import javax.swing.KeyStroke
3235

3336
/**
3437
* DevIn language input component for mpp-idea module.
35-
*
38+
*
3639
* Features:
3740
* - DevIn language support with syntax highlighting and completion
3841
* - Enter to submit, Shift/Ctrl/Cmd+Enter for newline
3942
* - Integration with IntelliJ's completion system (lookup listener)
43+
* - Auto-completion for @, /, $, : characters
4044
* - Placeholder text support
41-
*
45+
*
4246
* Based on AutoDevInput from core module but adapted for standalone mpp-idea usage.
4347
*/
4448
class IdeaDevInInput(
45-
project: Project,
49+
private val project: Project,
4650
private val listeners: List<DocumentListener> = emptyList(),
4751
val disposable: Disposable?,
4852
private val showAgent: Boolean = true
@@ -191,13 +195,18 @@ class IdeaDevInInput(
191195
}
192196
}
193197

194-
// Create new document using EditorFactory
198+
// Create new document with DevIn language support
199+
val id = UUID.randomUUID()
195200
val document = ReadAction.compute<Document, Throwable> {
196-
EditorFactory.getInstance().createDocument("")
201+
val psiFile = PsiFileFactory.getInstance(project)
202+
.createFileFromText("IdeaDevInInput-$id.devin", DevInLanguage, "")
203+
PsiDocumentManager.getInstance(project).getDocument(psiFile)
197204
}
198205

199-
initializeDocumentListeners(document)
200-
setDocument(document)
206+
if (document != null) {
207+
initializeDocumentListeners(document)
208+
setDocument(document)
209+
}
201210
}
202211

203212
private fun initializeDocumentListeners(inputDocument: Document) {
@@ -224,11 +233,12 @@ class IdeaDevInInput(
224233

225234
/**
226235
* Append text at the end of the document.
236+
* Uses InsertUtil for proper text insertion with DevIn language support.
227237
*/
228238
fun appendText(textToAppend: String) {
229239
WriteCommandAction.runWriteCommandAction(project, "Append text", "intentions.write.action", {
230240
val document = this.editor?.document ?: return@runWriteCommandAction
231-
document.insertString(document.textLength, textToAppend)
241+
InsertUtil.insertStringAndSaveChange(project, textToAppend, document, document.textLength, false)
232242
})
233243
}
234244

0 commit comments

Comments
 (0)