Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 27 additions & 3 deletions mpp-idea/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ plugins {
kotlin("jvm")
id("org.jetbrains.intellij.platform")
kotlin("plugin.compose")
kotlin("plugin.serialization")
}

group = "cc.unitmesh.devins"
Expand Down Expand Up @@ -102,15 +101,40 @@ dependencies {
}

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

// Gson for JSON serialization (used by IdeaRemoteAgentClient)
compileOnly("com.google.code.gson:gson:2.11.0")

// Note: We use SimpleJewelMarkdown with intellij-markdown parser instead of mikepenz
// to avoid Compose runtime version mismatch with IntelliJ's bundled Compose

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

// DevIn language support for @ and / completion
// These provide the DevIn language parser, completion contributors, and core functionality
implementation("AutoDev-Intellij:exts-devins-lang:$mppVersion") {
// Exclude kotlinx libraries - IntelliJ provides its own
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-core")
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-core-jvm")
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-swing")
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-serialization-json")
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-serialization-json-jvm")
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-serialization-core")
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-serialization-core-jvm")
}
implementation("AutoDev-Intellij:core:$mppVersion") {
// Exclude kotlinx libraries - IntelliJ provides its own
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-core")
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-core-jvm")
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-coroutines-swing")
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-serialization-json")
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-serialization-json-jvm")
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-serialization-core")
exclude(group = "org.jetbrains.kotlinx", module = "kotlinx-serialization-core-jvm")
}

// Ktor HTTP Client for LLM API calls - use compileOnly for libraries that may conflict
compileOnly("io.ktor:ktor-client-core:3.2.2")
compileOnly("io.ktor:ktor-client-cio:3.2.2")
Expand All @@ -128,7 +152,7 @@ dependencies {
// Target IntelliJ IDEA 2025.2+ for Compose support
create("IC", "2025.2.1")

bundledPlugins("com.intellij.java")
bundledPlugins("com.intellij.java", "org.intellij.plugins.markdown", "com.jetbrains.sh", "Git4Idea")

// Compose support dependencies (bundled in IDEA 252+)
bundledModules(
Expand Down
15 changes: 15 additions & 0 deletions mpp-idea/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Gradle JVM memory settings
org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1g -Dfile.encoding=UTF-8

# Kotlin daemon memory settings
kotlin.daemon.jvmargs=-Xmx4g

# Enable Gradle Configuration Cache
org.gradle.configuration-cache = true

# Enable Gradle Build Cache
org.gradle.caching = true

# Kotlin stdlib
kotlin.stdlib.default.dependency = false

9 changes: 6 additions & 3 deletions mpp-idea/settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ pluginManagement {
}

plugins {
kotlin("jvm") version "2.1.20"
kotlin("plugin.compose") version "2.1.20"
kotlin("plugin.serialization") version "2.1.20"
kotlin("jvm") version "2.2.0"
kotlin("plugin.compose") version "2.2.0"
id("org.jetbrains.intellij.platform") version "2.10.2"
}
}
Expand All @@ -24,13 +23,17 @@ pluginManagement {
// - mpp-core: group = "cc.unitmesh"
// - mpp-codegraph: uses root project name
// - mpp-viewer: group = "cc.unitmesh.viewer"
// - devins-lang, core: uses root project name "AutoDev-Intellij" as group
includeBuild("..") {
dependencySubstitution {
// Substitute Maven coordinates with project dependencies
substitute(module("AutoDev-Intellij:mpp-ui")).using(project(":mpp-ui")).because("Using local project")
substitute(module("cc.unitmesh:mpp-core")).using(project(":mpp-core")).because("Using local project")
substitute(module("AutoDev-Intellij:mpp-codegraph")).using(project(":mpp-codegraph")).because("Using local project")
substitute(module("cc.unitmesh.viewer:mpp-viewer")).using(project(":mpp-viewer")).because("Using local project")
// DevIn language support for @ and / completion
substitute(module("AutoDev-Intellij:exts-devins-lang")).using(project(":exts:devins-lang")).because("Using local project")
substitute(module("AutoDev-Intellij:core")).using(project(":core")).because("Using local project")
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,22 @@ import org.jetbrains.jewel.ui.component.Icon

/**
* Bottom toolbar for the input section.
* Provides send/stop buttons, @ trigger for agent completion, / command trigger, model selector, settings, and token info.
* Provides send/stop buttons, model selector, settings, and token info.
*
* Layout: Workspace - Token Info - ModelSelector - @ Symbol - / Symbol - Settings - Send Button
* Layout: ModelSelector - Token Info | MCP Settings - Prompt Optimization - Send Button
* - Left side: Model configuration (blends with background)
* - Right side: MCP, prompt optimization, and send
*
* Uses Jewel components for native IntelliJ IDEA look and feel.
* Note: @ and / triggers are now in the top toolbar (IdeaTopToolbar).
*/
@Composable
fun IdeaBottomToolbar(
onSendClick: () -> Unit,
sendEnabled: Boolean,
isExecuting: Boolean = false,
onStopClick: () -> Unit = {},
onAtClick: () -> Unit = {},
onSlashClick: () -> Unit = {},
onSettingsClick: () -> Unit = {},
workspacePath: String? = null,
onPromptOptimizationClick: () -> Unit = {},
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The onPromptOptimizationClick parameter in IdeaBottomToolbar is not connected to any functionality. The prompt optimization dialog (IdeaPromptOptimizationDialog) is not instantiated or triggered anywhere in the codebase, making this button non-functional.

Copilot uses AI. Check for mistakes.
totalTokens: Int? = null,
// Model selector props
availableConfigs: List<NamedModelConfig> = emptyList(),
Expand All @@ -46,74 +46,33 @@ fun IdeaBottomToolbar(
Row(
modifier = modifier
.fillMaxWidth()
.padding(horizontal = 8.dp, vertical = 6.dp),
.padding(horizontal = 4.dp, vertical = 4.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
// Left side: workspace and token info
// Left side: Model selector and token info
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.weight(1f, fill = false)
) {
// Workspace indicator
if (!workspacePath.isNullOrEmpty()) {
// Extract project name from path, handling both Unix and Windows separators
val projectName = workspacePath
.replace('\\', '/') // Normalize to Unix separator
.substringAfterLast('/')
.ifEmpty { "Project" }

Box(
modifier = Modifier
.clip(RoundedCornerShape(4.dp))
.background(JewelTheme.globalColors.panelBackground.copy(alpha = 0.8f))
.padding(horizontal = 8.dp, vertical = 4.dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
Icon(
imageVector = IdeaComposeIcons.Folder,
contentDescription = null,
tint = JewelTheme.globalColors.text.normal,
modifier = Modifier.size(12.dp)
)
Text(
text = projectName,
style = JewelTheme.defaultTextStyle.copy(fontSize = 12.sp),
maxLines = 1
)
}
}
}
// Model selector (transparent, blends with background)
IdeaModelSelector(
availableConfigs = availableConfigs,
currentConfigName = currentConfigName,
onConfigSelect = onConfigSelect,
onConfigureClick = onConfigureClick
)

// Token usage indicator
// Token usage indicator (subtle)
if (totalTokens != null && totalTokens > 0) {
Box(
modifier = Modifier
.clip(RoundedCornerShape(4.dp))
.background(AutoDevColors.Blue.c400.copy(alpha = 0.2f))
.padding(horizontal = 8.dp, vertical = 4.dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(
text = "Token",
style = JewelTheme.defaultTextStyle.copy(fontSize = 11.sp)
)
Text(
text = "$totalTokens",
style = JewelTheme.defaultTextStyle.copy(
fontSize = 11.sp,
fontWeight = FontWeight.Bold
)
)
}
}
Text(
text = "${totalTokens}t",
style = JewelTheme.defaultTextStyle.copy(
fontSize = 11.sp,
color = JewelTheme.globalColors.text.normal.copy(alpha = 0.6f)
)
)
}
}

Expand All @@ -122,49 +81,27 @@ fun IdeaBottomToolbar(
horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalAlignment = Alignment.CenterVertically
) {
// Model selector
IdeaModelSelector(
availableConfigs = availableConfigs,
currentConfigName = currentConfigName,
onConfigSelect = onConfigSelect,
onConfigureClick = onConfigureClick
)

// @ trigger button for agent completion
// MCP Settings button
IconButton(
onClick = onAtClick,
onClick = onSettingsClick,
modifier = Modifier.size(32.dp)
) {
Icon(
imageVector = IdeaComposeIcons.AlternateEmail,
contentDescription = "@ Agent",
imageVector = IdeaComposeIcons.Settings,
contentDescription = "MCP Settings",
tint = JewelTheme.globalColors.text.normal,
modifier = Modifier.size(18.dp)
)
}

// / trigger button for slash commands
IconButton(
onClick = onSlashClick,
modifier = Modifier.size(32.dp)
) {
Text(
text = "/",
style = JewelTheme.defaultTextStyle.copy(
fontSize = 16.sp,
fontWeight = FontWeight.Bold
)
modifier = Modifier.size(16.dp)
)
}

// Settings button
// Prompt Optimization button
IconButton(
onClick = onSettingsClick,
onClick = onPromptOptimizationClick,
modifier = Modifier.size(32.dp)
) {
Icon(
imageVector = IdeaComposeIcons.Settings,
contentDescription = "Settings",
imageVector = IdeaComposeIcons.AutoAwesome,
contentDescription = "Prompt Optimization",
tint = JewelTheme.globalColors.text.normal,
modifier = Modifier.size(16.dp)
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package cc.unitmesh.devins.idea.editor

import cc.unitmesh.devti.language.DevInLanguage
import cc.unitmesh.devti.util.InsertUtil
import com.intellij.codeInsight.AutoPopupController
import com.intellij.codeInsight.lookup.LookupManagerListener
import com.intellij.lang.Language
import com.intellij.openapi.Disposable
import com.intellij.openapi.actionSystem.CustomShortcutSet
import com.intellij.openapi.actionSystem.KeyboardShortcut
import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.editor.Document
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.EditorFactory
import com.intellij.openapi.editor.EditorModificationUtil
import com.intellij.openapi.application.ReadAction
import com.intellij.openapi.application.runReadAction
Expand All @@ -21,6 +22,8 @@ import com.intellij.openapi.fileTypes.FileTypes
import com.intellij.openapi.project.DumbAwareAction
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiFileFactory
import com.intellij.testFramework.LightVirtualFile
import com.intellij.ui.EditorTextField
import com.intellij.util.EventDispatcher
Expand All @@ -32,17 +35,18 @@ import javax.swing.KeyStroke

/**
* DevIn language input component for mpp-idea module.
*
*
* Features:
* - DevIn language support with syntax highlighting and completion
* - Enter to submit, Shift/Ctrl/Cmd+Enter for newline
* - Integration with IntelliJ's completion system (lookup listener)
* - Auto-completion for @, /, $, : characters
* - Placeholder text support
*
*
* Based on AutoDevInput from core module but adapted for standalone mpp-idea usage.
*/
class IdeaDevInInput(
project: Project,
private val project: Project,
private val listeners: List<DocumentListener> = emptyList(),
val disposable: Disposable?,
private val showAgent: Boolean = true
Expand Down Expand Up @@ -191,13 +195,18 @@ class IdeaDevInInput(
}
}

// Create new document using EditorFactory
// Create new document with DevIn language support
val id = UUID.randomUUID()
val document = ReadAction.compute<Document, Throwable> {
EditorFactory.getInstance().createDocument("")
val psiFile = PsiFileFactory.getInstance(project)
.createFileFromText("IdeaDevInInput-$id.devin", DevInLanguage, "")
PsiDocumentManager.getInstance(project).getDocument(psiFile)
}

initializeDocumentListeners(document)
setDocument(document)
if (document != null) {
initializeDocumentListeners(document)
setDocument(document)
}
}

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

/**
* Append text at the end of the document.
* Uses InsertUtil for proper text insertion with DevIn language support.
*/
fun appendText(textToAppend: String) {
WriteCommandAction.runWriteCommandAction(project, "Append text", "intentions.write.action", {
val document = this.editor?.document ?: return@runWriteCommandAction
document.insertString(document.textLength, textToAppend)
InsertUtil.insertStringAndSaveChange(project, textToAppend, document, document.textLength, false)
})
}

Expand Down
Loading
Loading