Skip to content

Commit 2172207

Browse files
committed
wip
1 parent e32249b commit 2172207

File tree

3 files changed

+97
-93
lines changed

3 files changed

+97
-93
lines changed

src/main/kotlin/aicoder/actions/agent/CommandAutofixAction.kt

Lines changed: 80 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,9 @@ import aicoder.actions.BaseAction
88
import aicoder.actions.SessionProxyServer
99
import com.intellij.openapi.actionSystem.ActionUpdateThread
1010
import com.intellij.openapi.actionSystem.AnActionEvent
11-
import com.intellij.openapi.actionSystem.PlatformDataKeys
1211
import com.intellij.openapi.project.Project
1312
import com.intellij.openapi.ui.ComboBox
1413
import com.intellij.openapi.ui.DialogWrapper
15-
import com.intellij.openapi.vfs.VirtualFile
1614
import com.simiacryptus.aicoder.AppServer
1715
import com.simiacryptus.aicoder.config.AppSettingsState
1816
import com.simiacryptus.aicoder.util.BrowseUtil.browse
@@ -21,12 +19,13 @@ import com.simiacryptus.jopenai.models.chatModel
2119
import com.simiacryptus.skyenet.apps.general.CmdPatchApp
2220
import com.simiacryptus.skyenet.apps.general.PatchApp
2321
import com.simiacryptus.skyenet.core.platform.Session
22+
import com.simiacryptus.skyenet.core.util.FileValidationUtils
23+
import com.simiacryptus.skyenet.core.util.commonRoot
2424
import com.simiacryptus.skyenet.webui.application.AppInfoData
2525
import com.simiacryptus.skyenet.webui.application.ApplicationServer
2626
import org.slf4j.LoggerFactory
2727
import java.awt.BorderLayout
2828
import java.io.File
29-
import java.nio.file.Files
3029
import java.text.SimpleDateFormat
3130
import javax.swing.*
3231
import kotlin.collections.set
@@ -50,61 +49,81 @@ class CommandAutofixAction : BaseAction() {
5049
UITools.runAsync(event.project, "Initializing Command Autofix", true) { progress ->
5150
progress.isIndeterminate = true
5251
progress.text = "Getting settings..."
53-
val settings = getUserSettings(event) ?: return@runAsync
54-
val dataContext = event.dataContext
55-
val virtualFiles = PlatformDataKeys.VIRTUAL_FILE_ARRAY.getData(dataContext)
56-
setupAndLaunchSession(event, settings, virtualFiles)
52+
val folders = UITools.getSelectedFolders(event).map { it.toFile.toPath() }
53+
val root = folders.toTypedArray().commonRoot()
54+
val files = folders.flatMap { FileValidationUtils.expandFileList(it.toFile()).toList() }.distinct().sorted().toTypedArray()
55+
val settings = run {
56+
var settings1: PatchApp.Settings? = null
57+
SwingUtilities.invokeAndWait {
58+
val settingsUI = SettingsUI(workingDirectory = root.toFile())
59+
val dialog = CommandSettingsDialog(event.project, settingsUI)
60+
dialog.show()
61+
settings1 = if (dialog.isOK) {
62+
val executable = File(settingsUI.commandField.selectedItem?.toString() ?: throw IllegalArgumentException("No executable selected"))
63+
AppSettingsState.instance.executables += executable.absolutePath
64+
val argument = settingsUI.argumentsField.selectedItem?.toString() ?: ""
65+
AppSettingsState.instance.recentArguments.remove(argument)
66+
AppSettingsState.instance.recentArguments.add(0, argument)
67+
AppSettingsState.instance.recentArguments =
68+
AppSettingsState.instance.recentArguments.take(MAX_RECENT_ARGUMENTS).toMutableList()
69+
val workingDir = settingsUI.workingDirectoryField.selectedItem?.toString() ?: ""
70+
AppSettingsState.instance.recentWorkingDirs.remove(workingDir)
71+
AppSettingsState.instance.recentWorkingDirs.add(0, workingDir)
72+
AppSettingsState.instance.recentWorkingDirs =
73+
AppSettingsState.instance.recentWorkingDirs.take(MAX_RECENT_DIRS).toMutableList()
74+
PatchApp.Settings(
75+
executable = executable,
76+
arguments = argument,
77+
workingDirectory = File(workingDir),
78+
exitCodeOption = if (settingsUI.exitCodeZero.isSelected) "0" else if (settingsUI.exitCodeAny.isSelected) "any" else "nonzero",
79+
additionalInstructions = settingsUI.additionalInstructionsField.text,
80+
autoFix = settingsUI.autoFixCheckBox.isSelected,
81+
maxRetries = settingsUI.maxRetriesField.value as Int,
82+
)
83+
} else {
84+
null
85+
}
86+
}
87+
settings1
88+
} ?: return@runAsync
89+
require(settings.executable.exists()) { "Executable file does not exist: ${settings.executable}" }
90+
val patchApp = CmdPatchApp(
91+
root = root,
92+
settings = settings,
93+
api = api,
94+
files = files,
95+
model = AppSettingsState.instance.smartModel.chatModel()
96+
)
97+
val session = Session.newGlobalID()
98+
SessionProxyServer.chats[session] = patchApp
99+
ApplicationServer.appInfoMap[session] = AppInfoData(
100+
applicationName = "Code Chat",
101+
singleInput = true,
102+
stickyInput = false,
103+
loadImages = false,
104+
showMenubar = false
105+
)
106+
val dateFormat = SimpleDateFormat("HH:mm:ss")
107+
val sessionName = "${javaClass.simpleName} @ ${dateFormat.format(System.currentTimeMillis())}"
108+
SessionProxyServer.metadataStorage.setSessionName(null, session, sessionName)
109+
val server = AppServer.getServer(event.project)
110+
Thread {
111+
Thread.sleep(500)
112+
try {
113+
val uri = server.server.uri.resolve("/#$session")
114+
BaseAction.log.info("Opening browser to $uri")
115+
browse(uri)
116+
} catch (e: Throwable) {
117+
log.warn("Error opening browser", e)
118+
}
119+
}.start()
57120
}
58121
} catch (e: Throwable) {
59122
log.error("Failed to execute command autofix", e)
60123
UITools.showErrorDialog("Failed to execute command autofix: ${e.message}", "Error")
61124
}
62125
}
63126

64-
/**
65-
* Sets up and launches the patch app session
66-
*/
67-
private fun setupAndLaunchSession(event: AnActionEvent, settings: PatchApp.Settings, virtualFiles: Array<VirtualFile>?) {
68-
val folder = UITools.getSelectedFolder(event)
69-
val root = if (null != folder) {
70-
folder.toFile.toPath()
71-
} else {
72-
event.project?.basePath?.let { File(it).toPath() }
73-
}!!
74-
// Validate input parameters
75-
require(settings.executable.exists()) { "Executable file does not exist: ${settings.executable}" }
76-
val patchApp = CmdPatchApp(
77-
root,
78-
settings,
79-
api,
80-
virtualFiles?.map { it.toFile }?.toTypedArray(),
81-
AppSettingsState.instance.smartModel.chatModel()
82-
)
83-
val session = Session.newGlobalID()
84-
SessionProxyServer.chats[session] = patchApp
85-
ApplicationServer.appInfoMap[session] = AppInfoData(
86-
applicationName = "Code Chat",
87-
singleInput = true,
88-
stickyInput = false,
89-
loadImages = false,
90-
showMenubar = false
91-
)
92-
val dateFormat = SimpleDateFormat("HH:mm:ss")
93-
val sessionName = "${javaClass.simpleName} @ ${dateFormat.format(System.currentTimeMillis())}"
94-
SessionProxyServer.metadataStorage.setSessionName(null, session, sessionName)
95-
val server = AppServer.getServer(event.project)
96-
Thread {
97-
Thread.sleep(500)
98-
try {
99-
val uri = server.server.uri.resolve("/#$session")
100-
BaseAction.log.info("Opening browser to $uri")
101-
browse(uri)
102-
} catch (e: Throwable) {
103-
log.warn("Error opening browser", e)
104-
}
105-
}.start()
106-
}
107-
108127
/**
109128
* Checks if the action should be enabled
110129
*/
@@ -119,50 +138,14 @@ class CommandAutofixAction : BaseAction() {
119138
private val log = LoggerFactory.getLogger(CommandAutofixAction::class.java)
120139
private const val DEFAULT_ARGUMENT = "run build"
121140
private const val MAX_RECENT_ARGUMENTS = 10
141+
private const val MAX_RECENT_DIRS = 10
122142
private const val TEXT_AREA_ROWS = 3
123143

124-
private fun getUserSettings(event: AnActionEvent?): PatchApp.Settings? {
125-
val root = UITools.getSelectedFolder(event ?: return null)?.toNioPath() ?: event.project?.basePath?.let {
126-
File(it).toPath()
127-
}
128-
val files = UITools.getSelectedFiles(event).map { it.path.let { File(it).toPath() } }.toMutableSet()
129-
if (files.isEmpty()) Files.walk(root)
130-
.filter { Files.isRegularFile(it) && !Files.isDirectory(it) }
131-
.toList().filterNotNull().forEach { files.add(it) }
132-
var settings: PatchApp.Settings? = null
133-
SwingUtilities.invokeAndWait {
134-
val settingsUI = SettingsUI(root!!.toFile())
135-
val dialog = CommandSettingsDialog(event.project, settingsUI)
136-
dialog.show()
137-
settings = if (dialog.isOK) {
138-
val executable = File(settingsUI.commandField.selectedItem?.toString() ?: throw IllegalArgumentException("No executable selected"))
139-
AppSettingsState.instance.executables += executable.absolutePath
140-
val argument = settingsUI.argumentsField.selectedItem?.toString() ?: ""
141-
AppSettingsState.instance.recentArguments.remove(argument)
142-
AppSettingsState.instance.recentArguments.add(0, argument)
143-
AppSettingsState.instance.recentArguments =
144-
AppSettingsState.instance.recentArguments.take(MAX_RECENT_ARGUMENTS).toMutableList()
145-
PatchApp.Settings(
146-
executable = executable,
147-
arguments = argument,
148-
workingDirectory = File(settingsUI.workingDirectoryField.text),
149-
exitCodeOption = if (settingsUI.exitCodeZero.isSelected) "0" else if (settingsUI.exitCodeAny.isSelected) "any" else "nonzero",
150-
additionalInstructions = settingsUI.additionalInstructionsField.text,
151-
autoFix = settingsUI.autoFixCheckBox.isSelected,
152-
maxRetries = settingsUI.maxRetriesField.value as Int,
153-
)
154-
} else {
155-
null
156-
}
157-
}
158-
return settings
159-
}
160-
161144
/**
162145
* UI component class for command settings dialog
163146
*/
164147

165-
class SettingsUI(root: File) {
148+
class SettingsUI(workingDirectory: File) {
166149
val maxRetriesField = JSpinner(SpinnerNumberModel(3, 0, 10, 1)).apply {
167150
toolTipText = "Maximum number of auto-retry attempts (0-10)"
168151
}
@@ -187,18 +170,23 @@ class CommandAutofixAction : BaseAction() {
187170
}
188171
}
189172
}
190-
val workingDirectoryField = JTextField(root.absolutePath).apply {
173+
val workingDirectoryField = ComboBox<String>().apply {
191174
isEditable = true
175+
AppSettingsState.instance.recentWorkingDirs.forEach { addItem(it) }
176+
if (AppSettingsState.instance.recentWorkingDirs.isEmpty()) {
177+
addItem(workingDirectory.absolutePath)
178+
}
179+
selectedItem = workingDirectory.absolutePath
192180
}
193181
val workingDirectoryButton = JButton("...").apply {
194182
addActionListener {
195183
val fileChooser = JFileChooser().apply {
196184
fileSelectionMode = JFileChooser.DIRECTORIES_ONLY
197185
isMultiSelectionEnabled = false
198-
this.selectedFile = File(workingDirectoryField.text)
186+
this.selectedFile = File(workingDirectoryField.selectedItem?.toString() ?: workingDirectory.absolutePath)
199187
}
200188
if (fileChooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
201-
workingDirectoryField.text = fileChooser.selectedFile.absolutePath
189+
workingDirectoryField.selectedItem = fileChooser.selectedFile.absolutePath
202190
}
203191
}
204192
}

src/main/kotlin/com/simiacryptus/aicoder/config/AppSettingsState.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ data class AppSettingsState(
5959
var enableLegacyActions: Boolean = false,
6060
var executables: MutableSet<String> = mutableSetOf(),
6161
var recentArguments: MutableList<String> = mutableListOf(),
62+
var recentWorkingDirs: MutableList<String> = mutableListOf(),
6263
val recentCommands: MutableMap<String, MRUItems> = mutableMapOf<String, MRUItems>(),
6364
var userSuppliedModels: MutableList<UserSuppliedModel> = mutableListOf(),
6465
var githubToken: String? = null,
@@ -208,4 +209,5 @@ data class AppSettingsState(
208209
)
209210

210211
var analyticsEnabled: Boolean = false
211-
}
212+
}
213+
var recentWorkingDirs: MutableList<String> = mutableListOf()

src/main/kotlin/com/simiacryptus/aicoder/util/UITools.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,20 @@ object UITools {
601601
return null
602602
}
603603

604+
fun getSelectedFolders(e: AnActionEvent): List<VirtualFile> {
605+
val dataContext = e.dataContext
606+
val data = PlatformDataKeys.VIRTUAL_FILE_ARRAY.getData(dataContext)
607+
if (null != data) return data.filter { it.isDirectory }
608+
val editor = PlatformDataKeys.EDITOR.getData(dataContext)
609+
if (editor != null) {
610+
val file = FileDocumentManager.getInstance().getFile(editor.document)
611+
if (file != null) {
612+
return listOf(file.parent)
613+
}
614+
}
615+
return emptyList()
616+
}
617+
604618
fun getSelectedFile(e: AnActionEvent): VirtualFile? {
605619
val dataContext = e.dataContext
606620
val data = PlatformDataKeys.VIRTUAL_FILE.getData(dataContext)

0 commit comments

Comments
 (0)