-
Notifications
You must be signed in to change notification settings - Fork 1
feat(mpp-idea): add file search popup and fix MCP config button #28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Add IdeaFileSearchPopup for adding files to context - Support searching project files using FilenameIndex - Prioritize recently opened files using EditorHistoryManager - Multi-select files with checkbox UI - Filter binary and ignored files - Update IdeaTopToolbar with file selection functionality - Add project parameter for file search - Add onFilesSelected callback for selected files - Show file search popup on Add File button click - Fix IdeaBottomToolbar right-side config button - Change from model config to MCP config dialog - MCP config dialog now opens directly from settings button - Update IdeaDevInInputArea in IdeaAgentApp - Add IdeaTopToolbar to input area - Manage selected files state - Append file paths to message on send (/file:path format)
WalkthroughRefactors toolbars and input area to centralize file selection: adds a file search popup and presentation models, replaces multiple top-toolbar actions with a single Add File flow, renames MCP Settings to MCP Configuration with an in-place dialog, and integrates selected files into the message-send flow. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant TopToolbar as Top Toolbar
participant FilePopup as File Search Popup
participant Project as Project (IDE)
participant DevIn as DevIn Input Area
participant MessageFlow as Message Composition
User->>TopToolbar: Click "Add File"
TopToolbar->>FilePopup: Show (project supplied)
FilePopup->>Project: Load recent files / search request
Project-->>FilePopup: Recent files / search results
User->>FilePopup: Select files & confirm
FilePopup-->>TopToolbar: onFilesSelected(files)
TopToolbar->>DevIn: Update selectedFiles state
User->>DevIn: Compose message and send
DevIn->>MessageFlow: Append /file:<path> for each selected file
MessageFlow->>DevIn: Send combined message
DevIn->>DevIn: Clear selectedFiles
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related issues
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review completed. 2 suggestions posted.
Comment augment review to trigger a new review at any time.
| val scope = GlobalSearchScope.projectScope(project) | ||
|
|
||
| try { | ||
| ApplicationManager.getApplication().runReadAction { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This executes an index search inside a blocking read action triggered from a LaunchedEffect, which likely runs on the EDT and can freeze the UI on large projects; this risks violating the platform’s EDT rules. (Guideline: edt_violations)
🤖 Was this useful? React with 👍 or 👎
| isExecuting: Boolean = false, | ||
| onStopClick: () -> Unit = {}, | ||
| onSettingsClick: () -> Unit = {}, | ||
| onMcpConfigClick: () -> Unit = {}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
onMcpConfigClick is declared but never used in this composable, which makes the API misleading for callers. (Guideline: kotlin_code_quality)
🤖 Was this useful? React with 👍 or 👎
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (7)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaBottomToolbar.kt (1)
36-36: Consider removing unused parameter.The
onMcpConfigClickparameter is never invoked since the MCP configuration dialog is now managed internally via theshowMcpConfigDialogstate. If this parameter was retained for API compatibility, consider documenting that intent; otherwise, it can be safely removed to reduce maintenance burden.mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaFileSearchPopup.kt (2)
38-61: Avoid duplicateloadRecentFilescalls and consider adding basic query debounceBoth
LaunchedEffect(Unit)andLaunchedEffect(searchQuery)callloadRecentFiles(project)on initial composition, which does the same work twice, and searches fire on every keystroke once length ≥ 2. Consider collapsing the initial load into the query-driven effect (e.g., seed query state or guard the second effect) and optionally adding a small debounce before callingsearchFilesto reduce redundant work on large projects.
231-281: Run file search off the UI thread and add minimal logging for failures
searchFileswrapsFilenameIndex.processFilesByNameinrunReadActionbut runs directly in theLaunchedEffectcoroutine, which is typically on the UI dispatcher for Compose. On large projects this can block the IDE. Also, bothloadRecentFilesandsearchFilesswallow all exceptions silently.Consider:
- Switching the search to a background dispatcher and using a non-blocking read action:
private fun searchFiles(project: Project, query: String): List<IdeaFilePresentation> { - val results = mutableListOf<IdeaFilePresentation>() - val scope = GlobalSearchScope.projectScope(project) + val results = mutableListOf<IdeaFilePresentation>() + val scope = GlobalSearchScope.projectScope(project) - try { - ApplicationManager.getApplication().runReadAction { - FilenameIndex.processFilesByName(query, false, scope) { file -> - if (canBeAdded(project, file) && results.size < 50) { - results.add(IdeaFilePresentation.from(project, file)) - } - results.size < 50 - } - } - } catch (e: Exception) { - // Ignore search errors - } + try { + ApplicationManager.getApplication().runReadAction { + FilenameIndex.processFilesByName(query, false, scope) { file -> + if (canBeAdded(project, file) && results.size < 50) { + results.add(IdeaFilePresentation.from(project, file)) + } + results.size < 50 + } + } + } catch (e: Exception) { + // TODO: replace with proper IntelliJ logger + // thisLogger().warn("Error searching files for query: $query", e) + }
- Applying similar minimal logging in
loadRecentFilesinstead of fully ignoring failures.This keeps the UI responsive and makes diagnosing plugin issues easier.
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaTopToolbar.kt (1)
56-64: Clarify the role ofonAddFileClicknow that selection flows viaonFilesSelectedThe Add File button now:
if (project != null) { showFileSearchPopup = true } onAddFileClick()With the new
onFilesSelectedcallback handling actual file selection fromIdeaFileSearchPopup,onAddFileClickappears to be an auxiliary hook (e.g., analytics or legacy behavior). If it no longer has a distinct responsibility, consider deprecating or removing it to keep the API focused; else, document its expected use to avoid confusion for future callers.mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentApp.kt (3)
334-357: File selection state handling looks solid; check popup/z-order setup
selectedFilesis managed idiomatically (immutable updates, dedup bypath) and theIdeaTopToolbarwiring for add/remove is clean and predictable.Because this composable also embeds a
SwingPanelandIdeaTopToolbarultimately shows a Compose-based file search popup, please double‑check that:
IdeaFileSearchPopupis built on Jewel popup/dialog primitives (e.g.,DialogWrapper/JewelPopupMenu, notandroidx.compose.ui.window.Popup), andJewelFlags.useCustomPopupRenderer = trueis enabled inIdeaAgentToolWindowFactory,to avoid popups being rendered behind the Swing content. Based on learnings, this is the recommended setup for
**/idea/**/*.kt.
377-391: Appending/file:lines on editor submit is correct but duplicates bottom‑toolbar logicThe new
onSubmitpath correctly appends one/file:<path>line per selected file and clearsselectedFilesafterwards, mirroring the bottom toolbar behavior.To keep these paths from drifting, consider extracting a small helper (e.g.,
fun buildFullText(text, selectedFiles)) and reusing it in bothonSubmitand the bottom toolbar handler.
420-437: Unify send condition with attachments and reduce duplication in bottom toolbarThe bottom toolbar send path correctly mirrors the editor submit logic for appending
/file:lines and clearingselectedFiles. Two optional improvements:
- Allow “files‑only” sends (if desired):
Right nowsendEnabledand the send guard both require non‑blank text. If you want users to be able to send just selected files, you could relax this:- onSendClick = { - val text = devInInput?.text?.trim() ?: inputText.trim() - if (text.isNotBlank() && !isProcessing) { + onSendClick = { + val text = devInInput?.text?.trim() ?: inputText.trim() + if ((text.isNotBlank() || selectedFiles.isNotEmpty()) && !isProcessing) { @@ - sendEnabled = inputText.isNotBlank() && !isProcessing, + sendEnabled = (inputText.isNotBlank() || selectedFiles.isNotEmpty()) && !isProcessing,
- Avoid duplicating message construction:
As with the editor submit path, consider factoring out thefilesText/fullTextconstruction into a shared helper to keep behavior in sync.Also, the comment says “MCP config is handled internally”, but
onConfigureClickis still threaded through toIdeaBottomToolbar. IfIdeaBottomToolbarno longer uses this callback, you can drop the parameter fromIdeaDevInInputAreato simplify the API; if it still does, it may be worth renaming/clarifying its purpose.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaBottomToolbar.kt(5 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaFileSearchPopup.kt(1 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaTopToolbar.kt(4 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentApp.kt(4 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.kt
📄 CodeRabbit inference engine (AGENTS.md)
**/*.kt: Useexpect/actualfor platform-specific code (e.g., file I/O on JVM/JS/Wasm) in Kotlin Multiplatform projects
Check export first if some functions are not working well with CLI (TypeScript)
In Kotlin/JS @JsExport: AvoidFlow, usePromiseinstead
In Kotlin/JS @JsExport: Use concrete classes as return types and parameter types; avoid interface types
For WASM platform, avoid using emoji and UTF-8 in code
Files:
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaBottomToolbar.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentApp.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaTopToolbar.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaFileSearchPopup.kt
**/idea/**/*.kt
📄 CodeRabbit inference engine (AGENTS.md)
**/idea/**/*.kt: For SwingPanel z-index issues with Compose popups, enable Jewel's custom popup renderer:JewelFlags.useCustomPopupRenderer = trueinIdeaAgentToolWindowFactory
For popup/dropdown menus in IntelliJ plugins, use Jewel'sPopupMenuinstead ofandroidx.compose.ui.window.Popup
For dialogs in IntelliJ plugins, use IntelliJ'sDialogWrapperwithorg.jetbrains.jewel.bridge.composeinstead ofandroidx.compose.ui.window.Dialog
Files:
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaBottomToolbar.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentApp.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaTopToolbar.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaFileSearchPopup.kt
🧠 Learnings (5)
📚 Learning: 2025-12-02T00:20:34.480Z
Learnt from: CR
Repo: phodal/auto-dev-sketch PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T00:20:34.480Z
Learning: Applies to **/idea/**/*.kt : For SwingPanel z-index issues with Compose popups, enable Jewel's custom popup renderer: `JewelFlags.useCustomPopupRenderer = true` in `IdeaAgentToolWindowFactory`
Applied to files:
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentApp.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaTopToolbar.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaFileSearchPopup.kt
📚 Learning: 2025-12-02T00:20:34.480Z
Learnt from: CR
Repo: phodal/auto-dev-sketch PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T00:20:34.480Z
Learning: Applies to **/idea/**/*.kt : For dialogs in IntelliJ plugins, use IntelliJ's `DialogWrapper` with `org.jetbrains.jewel.bridge.compose` instead of `androidx.compose.ui.window.Dialog`
Applied to files:
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentApp.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaTopToolbar.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaFileSearchPopup.kt
📚 Learning: 2025-12-02T00:20:34.480Z
Learnt from: CR
Repo: phodal/auto-dev-sketch PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T00:20:34.480Z
Learning: Applies to **/idea/**/*.kt : For popup/dropdown menus in IntelliJ plugins, use Jewel's `PopupMenu` instead of `androidx.compose.ui.window.Popup`
Applied to files:
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentApp.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaTopToolbar.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaFileSearchPopup.kt
📚 Learning: 2025-12-02T00:20:34.480Z
Learnt from: CR
Repo: phodal/auto-dev-sketch PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T00:20:34.480Z
Learning: Applies to **/compose/**/*.{kt,kts} : In Compose (Desktop/Android), use `AutoDevColors` from `cc.unitmesh.devins.ui.compose.theme` or `MaterialTheme.colorScheme`
Applied to files:
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaTopToolbar.kt
📚 Learning: 2025-12-02T00:20:34.480Z
Learnt from: CR
Repo: phodal/auto-dev-sketch PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T00:20:34.480Z
Learning: Applies to {**/compose/**/*.kt,mpp-ui/src/jsMain/typescript/**/*.{ts,tsx}} : DO NOT hardcode colors (e.g., `Color(0xFF...)` or `#hex`). Always use design tokens for consistency across platforms
Applied to files:
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaTopToolbar.kt
🧬 Code graph analysis (3)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaBottomToolbar.kt (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaMcpConfigDialog.kt (1)
IdeaMcpConfigDialog(64-328)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentApp.kt (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaTopToolbar.kt (1)
IdeaTopToolbar(31-100)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaTopToolbar.kt (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaFileSearchPopup.kt (1)
IdeaFileSearchPopup(32-140)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Build (241)
- GitHub Check: Build (223)
🔇 Additional comments (8)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaBottomToolbar.kt (4)
22-29: LGTM! Documentation accurately reflects the MCP configuration changes.The updated comments clearly describe the new MCP configuration functionality and maintain consistency with the code changes.
46-46: LGTM! Proper Compose state management.The state variable correctly manages dialog visibility using standard Compose patterns.
85-96: LGTM! Button correctly triggers dialog.The MCP Configuration button properly opens the dialog by updating the state variable. The icon, description, and styling are appropriate.
160-167: Replaceandroidx.compose.ui.window.DialogwithDialogWrapperfor IntelliJ plugin compliance.The
IdeaMcpConfigDialogat line 173 usesandroidx.compose.ui.window.Dialog, which violates IntelliJ plugin guidelines. Per coding standards, IntelliJ plugin dialogs must useDialogWrapperwithorg.jetbrains.jewel.bridge.composeto ensure proper IDE integration and avoid rendering issues.Replace the direct
Dialogcomposable withDialogWrapperfor IDE UI framework compliance.⛔ Skipped due to learnings
Learnt from: CR Repo: phodal/auto-dev-sketch PR: 0 File: AGENTS.md:0-0 Timestamp: 2025-12-02T00:20:34.480Z Learning: Applies to **/idea/**/*.kt : For dialogs in IntelliJ plugins, use IntelliJ's `DialogWrapper` with `org.jetbrains.jewel.bridge.compose` instead of `androidx.compose.ui.window.Dialog`Learnt from: CR Repo: phodal/auto-dev-sketch PR: 0 File: AGENTS.md:0-0 Timestamp: 2025-12-02T00:20:34.480Z Learning: Applies to **/idea/**/*.kt : For SwingPanel z-index issues with Compose popups, enable Jewel's custom popup renderer: `JewelFlags.useCustomPopupRenderer = true` in `IdeaAgentToolWindowFactory`Learnt from: CR Repo: phodal/auto-dev-sketch PR: 0 File: AGENTS.md:0-0 Timestamp: 2025-12-02T00:20:34.480Z Learning: Applies to **/idea/**/*.kt : For popup/dropdown menus in IntelliJ plugins, use Jewel's `PopupMenu` instead of `androidx.compose.ui.window.Popup`mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaFileSearchPopup.kt (1)
142-199: File list item composition and selection wiring look solidThe row-level
clickableand checkbox both delegate to the sameonClick, visual selection state is derived fromisSelected, and Jewel theme tokens are used consistently. This item should behave as expected in the list with good UX.mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaTopToolbar.kt (2)
31-43: API extension for project and file selection callback is reasonableAdding
project: Project? = null,onFilesSelected: (List<VirtualFile>) -> Unit = {}, and internalshowFileSearchPopupkeeps the signature source-compatible while enabling the new file search flow. This is a clean way to evolve the toolbar without breaking existing callers.
89-99: Popup wiring and dismissal behavior look correctThe conditional popup:
- Guards on both
showFileSearchPopupandproject != null.- Properly closes on dismiss and after
onFilesSelected(files).This ensures the dialog lifecycle is tied to toolbar state and avoids null-project misuse.
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentApp.kt (1)
16-17: New editor imports are consistent with later usage
IdeaTopToolbarandSelectedFileItemare imported and used as expected inIdeaDevInInputArea; nothing to change here.
| Dialog(onDismissRequest = onDismiss) { | ||
| Column( | ||
| modifier = Modifier | ||
| .width(500.dp) | ||
| .height(400.dp) | ||
| .clip(RoundedCornerShape(8.dp)) | ||
| .background(JewelTheme.globalColors.panelBackground) | ||
| .padding(16.dp) | ||
| ) { | ||
| // Header | ||
| Row( | ||
| modifier = Modifier.fillMaxWidth(), | ||
| horizontalArrangement = Arrangement.SpaceBetween, | ||
| verticalAlignment = Alignment.CenterVertically | ||
| ) { | ||
| Text("Add Files to Context", style = JewelTheme.defaultTextStyle.copy(fontSize = 16.sp)) | ||
| IconButton(onClick = onDismiss) { | ||
| Icon( | ||
| imageVector = IdeaComposeIcons.Close, | ||
| contentDescription = "Close", | ||
| tint = JewelTheme.globalColors.text.normal, | ||
| modifier = Modifier.size(16.dp) | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| Spacer(modifier = Modifier.height(12.dp)) | ||
|
|
||
| // Search field using Jewel's TextField with TextFieldState | ||
| TextField( | ||
| state = searchQueryState, | ||
| placeholder = { Text("Search files...") }, | ||
| modifier = Modifier.fillMaxWidth() | ||
| ) | ||
|
|
||
| Spacer(modifier = Modifier.height(12.dp)) | ||
|
|
||
| // File list | ||
| if (isLoading) { | ||
| Box(modifier = Modifier.fillMaxWidth().weight(1f), contentAlignment = Alignment.Center) { | ||
| Text("Loading...") | ||
| } | ||
| } else { | ||
| LazyColumn(modifier = Modifier.fillMaxWidth().weight(1f)) { | ||
| items(searchResults) { file -> | ||
| FileListItem( | ||
| file = file, | ||
| isSelected = file.virtualFile in selectedFiles, | ||
| onClick = { | ||
| selectedFiles = if (file.virtualFile in selectedFiles) { | ||
| selectedFiles - file.virtualFile | ||
| } else { | ||
| selectedFiles + file.virtualFile | ||
| } | ||
| } | ||
| ) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| Spacer(modifier = Modifier.height(12.dp)) | ||
|
|
||
| // Footer with action buttons | ||
| Row( | ||
| modifier = Modifier.fillMaxWidth(), | ||
| horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.End) | ||
| ) { | ||
| OutlinedButton(onClick = onDismiss) { Text("Cancel") } | ||
| DefaultButton( | ||
| onClick = { onFilesSelected(selectedFiles.toList()) }, | ||
| enabled = selectedFiles.isNotEmpty() | ||
| ) { | ||
| Text("Add ${if (selectedFiles.isNotEmpty()) "(${selectedFiles.size})" else ""}") | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Replace Compose Dialog with IntelliJ DialogWrapper + Jewel bridge
This dialog is using androidx.compose.ui.window.Dialog, which is discouraged for IntelliJ plugins because of focus/z-order and window management issues. The project guidelines specify using IntelliJ’s DialogWrapper with org.jetbrains.jewel.bridge.compose for dialogs instead.
Refactor this to a DialogWrapper-backed dialog, e.g.:
- Introduce a
IdeaFileSearchDialog(project, onFilesSelected)class extendingDialogWrapperand building its center panel with the Jewel compose bridge. - Extract the current Column UI into a separate
@Composable(e.g.,IdeaFileSearchContent(...)) used inside that bridge. - From callers (like
IdeaTopToolbar), open the dialog via the new wrapper instead of a composableDialog.
This will align with IntelliJ’s dialog lifecycle and avoid Swing/Compose interop glitches. As per coding guidelines / learnings for **/idea/**/*.kt.
- Fix EDT violation: move searchAllItems to background thread using Dispatchers.IO - Remove unused onMcpConfigClick parameter from IdeaBottomToolbar - Add History icon to IdeaComposeIcons for recently opened files menu
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaFileSearchPopup.kt (2)
387-410: Consider using suspendingreadActioninstead of blockingrunReadAction.Since
searchAllItemsis called from a coroutine context onDispatchers.IO, you can use the suspendingreadActionfunction (already imported at line 15) instead of the blockingrunReadAction. This is more idiomatic for coroutine-based code.Apply this diff:
try { - ApplicationManager.getApplication().runReadAction { + readAction { // Search files by name FilenameIndex.processFilesByName(query, false, scope) { file -> if (file.isDirectory) {Note: You'll need to mark this function as
suspend:-private fun searchAllItems(project: Project, query: String): SearchResults { +private suspend fun searchAllItems(project: Project, query: String): SearchResults {
416-418: Optional: Avoid redundantloadRecentFilescall.
loadRecentFilesis already called in the initialLaunchedEffect(Unit)at line 73. When a search query is entered, this function is called again at line 416, unnecessarily re-loading the recent files. Consider passing the already-loaded recent files as a parameter tosearchAllItemsor filtering them in the caller.Example refactor:
-private suspend fun searchAllItems(project: Project, query: String): SearchResults { +private suspend fun searchAllItems(project: Project, query: String, recentFiles: List<IdeaFilePresentation>): SearchResults { val files = mutableListOf<IdeaFilePresentation>() val folders = mutableListOf<IdeaFilePresentation>() val scope = GlobalSearchScope.projectScope(project) val lowerQuery = query.lowercase() // ... existing search logic ... // Filter recent files by query - val recentFiles = loadRecentFiles(project).filter { + val filteredRecent = recentFiles.filter { it.name.lowercase().contains(lowerQuery) || it.presentablePath.lowercase().contains(lowerQuery) } return SearchResults( files = files.sortedBy { it.name }, folders = folders.sortedBy { it.presentablePath }, - recentFiles = recentFiles + recentFiles = filteredRecent ) }Then update the call at line 61:
- searchAllItems(project, searchQuery) + searchAllItems(project, searchQuery, recentFiles)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaBottomToolbar.kt(4 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaFileSearchPopup.kt(1 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaComposeIcons.kt(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaBottomToolbar.kt
🧰 Additional context used
📓 Path-based instructions (2)
**/*.kt
📄 CodeRabbit inference engine (AGENTS.md)
**/*.kt: Useexpect/actualfor platform-specific code (e.g., file I/O on JVM/JS/Wasm) in Kotlin Multiplatform projects
Check export first if some functions are not working well with CLI (TypeScript)
In Kotlin/JS @JsExport: AvoidFlow, usePromiseinstead
In Kotlin/JS @JsExport: Use concrete classes as return types and parameter types; avoid interface types
For WASM platform, avoid using emoji and UTF-8 in code
Files:
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaFileSearchPopup.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaComposeIcons.kt
**/idea/**/*.kt
📄 CodeRabbit inference engine (AGENTS.md)
**/idea/**/*.kt: For SwingPanel z-index issues with Compose popups, enable Jewel's custom popup renderer:JewelFlags.useCustomPopupRenderer = trueinIdeaAgentToolWindowFactory
For popup/dropdown menus in IntelliJ plugins, use Jewel'sPopupMenuinstead ofandroidx.compose.ui.window.Popup
For dialogs in IntelliJ plugins, use IntelliJ'sDialogWrapperwithorg.jetbrains.jewel.bridge.composeinstead ofandroidx.compose.ui.window.Dialog
Files:
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaFileSearchPopup.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaComposeIcons.kt
🧠 Learnings (5)
📚 Learning: 2025-12-02T00:20:34.480Z
Learnt from: CR
Repo: phodal/auto-dev-sketch PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T00:20:34.480Z
Learning: Applies to **/idea/**/*.kt : For popup/dropdown menus in IntelliJ plugins, use Jewel's `PopupMenu` instead of `androidx.compose.ui.window.Popup`
Applied to files:
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaFileSearchPopup.kt
📚 Learning: 2025-12-02T00:20:34.480Z
Learnt from: CR
Repo: phodal/auto-dev-sketch PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T00:20:34.480Z
Learning: Applies to **/idea/**/*.kt : For SwingPanel z-index issues with Compose popups, enable Jewel's custom popup renderer: `JewelFlags.useCustomPopupRenderer = true` in `IdeaAgentToolWindowFactory`
Applied to files:
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaFileSearchPopup.kt
📚 Learning: 2025-12-02T00:20:34.480Z
Learnt from: CR
Repo: phodal/auto-dev-sketch PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T00:20:34.480Z
Learning: Applies to **/idea/**/*.kt : For dialogs in IntelliJ plugins, use IntelliJ's `DialogWrapper` with `org.jetbrains.jewel.bridge.compose` instead of `androidx.compose.ui.window.Dialog`
Applied to files:
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaFileSearchPopup.kt
📚 Learning: 2025-12-02T00:20:34.480Z
Learnt from: CR
Repo: phodal/auto-dev-sketch PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T00:20:34.480Z
Learning: Applies to **/compose/**/*.{kt,kts} : In Compose (Desktop/Android), use `AutoDevColors` from `cc.unitmesh.devins.ui.compose.theme` or `MaterialTheme.colorScheme`
Applied to files:
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaFileSearchPopup.kt
📚 Learning: 2025-12-02T00:20:34.480Z
Learnt from: CR
Repo: phodal/auto-dev-sketch PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T00:20:34.480Z
Learning: Applies to {**/compose/**/*.kt,mpp-ui/src/jsMain/typescript/**/*.{ts,tsx}} : DO NOT hardcode colors (e.g., `Color(0xFF...)` or `#hex`). Always use design tokens for consistency across platforms
Applied to files:
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaFileSearchPopup.kt
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Agent
- GitHub Check: Build (223)
- GitHub Check: Build (241)
🔇 Additional comments (4)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaComposeIcons.kt (1)
1543-1584: LGTM!The History icon implementation follows the established pattern in this file perfectly. The icon is properly documented, uses lazy initialization, and maintains consistency with dimensions (24dp × 24dp), viewport (24×24), and styling (black fill) used by all other icons in this provider.
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaFileSearchPopup.kt (3)
38-268: LGTM! Correct use of Jewel's PopupMenu for IntelliJ plugin.The implementation correctly uses
PopupMenufrom Jewel for the file search popup, which aligns with the coding guidelines for popup/dropdown menus in IntelliJ plugins. The structure and interaction patterns are well-designed.As per coding guidelines and learnings for
**/idea/**/*.kt.
58-76: LGTM! Proper EDT offloading with IO dispatcher.Both search operations and initial file loading correctly use
withContext(Dispatchers.IO)to avoid blocking the UI thread. This ensures responsive UI even with large projects.
270-441: LGTM! Well-structured data models and helper functions.The helper composables, data classes, and utility functions are well-designed:
IdeaFilePresentationprovides a clean abstraction with a convenient factory methodloadRecentFilesproperly handles errors and filters invalid filescanBeAddedincludes comprehensive filtering for binary files, ignored files, and files outside project content- The binary extension list is reasonable and covers common cases
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds file search functionality to the IDEA plugin's input area and reorganizes the toolbar configuration. The changes introduce a new file search popup using IntelliJ's FilenameIndex API, integrate it with the top toolbar, and shift the settings button from model configuration to MCP configuration to avoid duplication (since the left side already has model selection).
- New Compose-based file search popup with recent files prioritization
- File selection chips UI with remove functionality
- MCP configuration now accessible directly from bottom toolbar
- Selected files automatically appended to messages using
/file:pathformat
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| IdeaComposeIcons.kt | Adds History icon for recent files display |
| IdeaFileSearchPopup.kt | New file search dialog with IntelliJ FilenameIndex integration and recent file support |
| IdeaTopToolbar.kt | Simplified to single "Add File" button, removed unused buttons, integrated file search popup |
| IdeaBottomToolbar.kt | Changed settings button from model config to MCP config dialog |
| IdeaAgentApp.kt | Added file selection state management and automatic file path appending to messages |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| * Contains @ trigger, file selection, and other context-related actions. | ||
| * | ||
| * | ||
| * Layout: @ - / - Clipboard - Save - Cursor | Selected Files... | Add |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment on line 29 describing the layout is outdated and no longer matches the actual implementation. The toolbar now only contains an "Add File" button, not "@ - / - Clipboard - Save - Cursor | Selected Files... | Add". Update the comment to reflect the current layout: "Add File | Selected Files..."
| * Layout: @ - / - Clipboard - Save - Cursor | Selected Files... | Add | |
| * Layout: Add File | Selected Files... |
| ApplicationManager.getApplication().runReadAction { | ||
| // Search files by name | ||
| FilenameIndex.processFilesByName(query, false, scope) { file -> | ||
| if (file.isDirectory) { | ||
| if (folders.size < 20) { | ||
| folders.add(IdeaFilePresentation.from(project, file)) | ||
| } | ||
| } else if (canBeAdded(project, file) && files.size < 50) { | ||
| files.add(IdeaFilePresentation.from(project, file)) | ||
| } | ||
| files.size < 50 || folders.size < 20 | ||
| } | ||
|
|
||
| // Also search for folders containing the query | ||
| val fileIndex = ProjectFileIndex.getInstance(project) | ||
| fileIndex.iterateContent { file -> | ||
| if (file.isDirectory && file.name.lowercase().contains(lowerQuery)) { | ||
| if (folders.size < 20 && !folders.any { it.path == file.path }) { | ||
| folders.add(IdeaFilePresentation.from(project, file)) | ||
| } | ||
| } | ||
| folders.size < 20 | ||
| } | ||
| } |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code performs read operations on IntelliJ's PSI/VFS inside a runReadAction block while already executing on the IO dispatcher via withContext(Dispatchers.IO). This is incorrect - runReadAction should be called directly from a suspending context (not from IO dispatcher), or you should use ApplicationManager.getApplication().executeOnPooledThread() for background operations.
The proper pattern for IntelliJ read actions in coroutines is to use readAction { } from com.intellij.openapi.application package (which is already imported but not used), which properly handles the dispatching. Change line 387 from ApplicationManager.getApplication().runReadAction { to readAction { and remove the withContext(Dispatchers.IO) wrapper at line 59, as readAction already runs on an appropriate background thread.
| } else if (canBeAdded(project, file) && files.size < 50) { | ||
| files.add(IdeaFilePresentation.from(project, file)) | ||
| } | ||
| files.size < 50 || folders.size < 20 |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic for continuing iteration is incorrect. The lambda should return true to continue processing and false to stop. Currently, line 397 returns true only when both conditions are met (files.size < 50 || folders.size < 20), meaning it stops when EITHER limit is reached. This should be && (AND) instead of || (OR) to continue only when BOTH haven't reached their limits yet, or return true unconditionally since you're already checking limits inside the conditions above.
| files.size < 50 || folders.size < 20 | |
| files.size < 50 && folders.size < 20 |
| folders.add(IdeaFilePresentation.from(project, file)) | ||
| } | ||
| } | ||
| folders.size < 20 |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The iteration should continue until the folders limit is reached, but this returns true only when folders.size < 20, which means it stops as soon as 20 folders are found. However, the logic seems backwards - the processor expects true to continue and false to stop. This should return true unconditionally or check folders.size < 20 to determine when to stop (return false when limit reached). The current logic will stop iteration prematurely once 20 folders are collected.
| val recentFiles = mutableListOf<IdeaFilePresentation>() | ||
|
|
||
| try { | ||
| val fileList = EditorHistoryManager.getInstance(project).fileList | ||
| fileList.take(30) | ||
| .filter { it.isValid && !it.isDirectory && canBeAdded(project, it) } | ||
| .forEach { file -> | ||
| recentFiles.add(IdeaFilePresentation.from(project, file, isRecent = true)) | ||
| } | ||
| } catch (e: Exception) { | ||
| // Ignore errors loading recent files | ||
| } | ||
|
|
||
| return recentFiles |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
loadRecentFiles is called inside withContext(Dispatchers.IO) but internally accesses IntelliJ APIs (EditorHistoryManager.getInstance(project).fileList) that should be accessed from a read action. These VFS operations need read access and should use readAction { } instead of running on the IO dispatcher. Consider wrapping the relevant operations in a readAction block or refactoring to use the proper threading model.
| val recentFiles = mutableListOf<IdeaFilePresentation>() | |
| try { | |
| val fileList = EditorHistoryManager.getInstance(project).fileList | |
| fileList.take(30) | |
| .filter { it.isValid && !it.isDirectory && canBeAdded(project, it) } | |
| .forEach { file -> | |
| recentFiles.add(IdeaFilePresentation.from(project, file, isRecent = true)) | |
| } | |
| } catch (e: Exception) { | |
| // Ignore errors loading recent files | |
| } | |
| return recentFiles | |
| return readAction { | |
| val recentFiles = mutableListOf<IdeaFilePresentation>() | |
| try { | |
| val fileList = EditorHistoryManager.getInstance(project).fileList | |
| fileList.take(30) | |
| .filter { it.isValid && !it.isDirectory && canBeAdded(project, it) } | |
| .forEach { file -> | |
| recentFiles.add(IdeaFilePresentation.from(project, file, isRecent = true)) | |
| } | |
| } catch (e: Exception) { | |
| // Ignore errors loading recent files | |
| } | |
| recentFiles | |
| } |
| } | ||
| onAddFileClick() |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The onAddFileClick() callback is invoked regardless of whether project is null. This means the callback is always triggered even when the file search popup cannot be shown (when project is null). Consider only calling onAddFileClick() when the popup can actually be displayed, or move it inside the if (project != null) block.
| } | |
| onAddFileClick() | |
| onAddFileClick() | |
| } |
| fun IdeaTopToolbar( | ||
| project: Project? = null, | ||
| onAtClick: () -> Unit = {}, | ||
| onSlashClick: () -> Unit = {}, |
Copilot
AI
Dec 2, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The parameter onSlashClick is defined but never used in the function body. All slash-related UI elements have been removed in this PR. Consider removing this unused parameter to clean up the API.
| onSlashClick: () -> Unit = {}, |
Summary
This PR adds file selection functionality to IdeaTopToolbar and fixes the right-side config button to open MCP configuration instead of model configuration.
Changes
1. IdeaFileSearchPopup (New)
FilenameIndexEditorHistoryManager2. IdeaTopToolbar
projectparameter for file searchonFilesSelectedcallback for selected files3. IdeaBottomToolbar
4. IdeaAgentApp
IdeaTopToolbarto input area/file:path)onSettingsClickparameter (MCP config handled internally)Testing
Pull Request opened by Augment Code with guidance from the PR author
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.