Skip to content

Commit

Permalink
find results edit - initial version
Browse files Browse the repository at this point in the history
  • Loading branch information
acharneski committed Dec 15, 2024
1 parent 3ef2742 commit 6673ba4
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,36 @@ package com.github.simiacryptus.aicoder.actions

import com.github.simiacryptus.aicoder.AppServer
import com.github.simiacryptus.aicoder.actions.find.FindResultsModificationDialog
import com.github.simiacryptus.aicoder.actions.generic.MultiDiffChatAction.Companion.patchEditorPrompt
import com.github.simiacryptus.aicoder.actions.generic.SessionProxyServer
import com.github.simiacryptus.aicoder.actions.generic.toFile
import com.github.simiacryptus.aicoder.config.AppSettingsState
import com.github.simiacryptus.aicoder.util.BrowseUtil.browse
import com.github.simiacryptus.aicoder.util.UITools
import com.github.simiacryptus.aicoder.util.psi.PsiUtil
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.editor.Document
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.findPsiFile
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiFile
import com.intellij.usages.Usage
import com.intellij.usages.UsageView
import com.simiacryptus.diff.addApplyFileDiffLinks
import com.simiacryptus.jopenai.models.chatModel
import com.simiacryptus.skyenet.TabbedDisplay
import com.simiacryptus.skyenet.core.actors.SimpleActor
import com.simiacryptus.skyenet.core.platform.Session
import com.simiacryptus.skyenet.core.platform.model.User
import com.simiacryptus.skyenet.core.util.getModuleRootForFile
import com.simiacryptus.skyenet.util.MarkdownUtil
import com.simiacryptus.skyenet.util.MarkdownUtil.renderMarkdown
import com.simiacryptus.skyenet.webui.application.AppInfoData
import com.simiacryptus.skyenet.webui.application.ApplicationServer
import com.simiacryptus.skyenet.webui.application.ApplicationSocketManager
import com.simiacryptus.skyenet.webui.session.SocketManager
import java.io.File
import java.nio.file.Path
import java.text.SimpleDateFormat
import java.util.*
import javax.swing.Icon

class FindResultsModificationAction(
Expand Down Expand Up @@ -99,23 +103,40 @@ class FindResultsModificationAction(
path = "/patchChat",
showMenubar = false,
) {
override val singleInput = true
override val stickyInput = false

override fun newSession(user: User?, session: Session): SocketManager {
val socketManager = super.newSession(user, session)
val ui = (socketManager as ApplicationSocketManager).applicationInterface
val task = ui.newTask()
val api = api.getChildClient().apply {
val createFile = task.createFile(".logs/api-${UUID.randomUUID()}.log")
createFile.second?.apply {
logStreams += this.outputStream().buffered()
task.verbose("API log: <a href=\"file:///$this\">$this</a>")
}
}
val tabs = TabbedDisplay(task)
usages.groupBy { it.location?.editor?.file }.entries.forEach { (file, usages) ->
val task = ui.newTask(false)
tabs[if (null == file) "Unknown" else file.name] = task.placeholder
val api = api.getChildClient().apply {
val createFile = task.createFile(".logs/api-${UUID.randomUUID()}.log")
createFile.second?.apply {
logStreams += this.outputStream().buffered()
task.verbose("API log: <a href=\"file:///$this\">$this</a>")
}
}
fun formatLine(index: Int, line: String, isFocused: Boolean) = when {
isFocused -> "/* L$index */ $line /* <<< */"
else -> "/* L$index */ $line"
}
val document = getDocument(project, file ?: return@forEach) ?: return@forEach
val document = PsiDocumentManager.getInstance(project).getDocument(file?.findPsiFile(project) ?: return@forEach) ?: return@forEach
val psiRoot: PsiFile? = file.findPsiFile(project)
val byContainer = usages.groupBy { getSmallestContainingEntity(psiRoot, it) }.entries.sortedBy { it.key?.textRange?.startOffset }.toTypedArray()
val lines = document.text.lines()
val filteredLines = lines.mapIndexedNotNull { index: Int, line: String ->
val filteredLines = lines.mapIndexed { index: Int, line: String ->
val lineStart = lines.subList(0, index).joinToString("\n").length
val lineEnd = lineStart + line.length
val containers = byContainer.map { it.key }.filter {
Expand All @@ -132,11 +153,39 @@ class FindResultsModificationAction(
} else if (containers.isNotEmpty()) {
formatLine(index, line, false)
} else {
null
"..."
}
}.joinToString("\n")
task.add(MarkdownUtil.renderMarkdown("## ${file.name}\n\n```${file.extension}\n$filteredLines\n```\n"))
// TODO: Ask simpleagent for change, and instrument diffs
}.joinToString("\n").replace("(?:\\.\\.\\.\n){2,}".toRegex(), "...\n")
val fileListingMarkdown = "## ${file.name}\n\n```${file.extension}\n$filteredLines\n```\n"
task.add(renderMarkdown(fileListingMarkdown))
val prompt = """
You are a code modification assistant. You will receive code files and locations where changes are needed.
Your task is to suggest appropriate modifications based on the replacement text provided.
Usage locations:
""".trimIndent() + usages.joinToString("\n") { "* `${it.presentation.plainText}`" } +
"\n\nRequested modification: " + modificationParams.replacementText + "\n\n" + patchEditorPrompt
ui.socketManager?.addApplyFileDiffLinks(
root = root.toPath(),
response = SimpleActor(
prompt = prompt,
model = AppSettingsState.instance.smartModel.chatModel()
).answer(
listOf(
fileListingMarkdown
), api
).replace(Regex("""/\* L\d+ \*/"""), "").replace(Regex("""/\* <<< \*/"""), ""),
handle = { newCodeMap ->
newCodeMap.forEach { (path, newCode) ->
task.complete("Updated $path")
}
},
ui = ui,
api = api,
shouldAutoApply = { modificationParams.autoApply },
defaultFile = file.toFile.path
)?.apply {
task.complete(renderMarkdown(this))
}
}
return socketManager
}
Expand All @@ -149,10 +198,6 @@ class FindResultsModificationAction(
)
}

private fun getDocument(project: Project, file: VirtualFile): Document? {
return PsiDocumentManager.getInstance(project).getDocument(file.findPsiFile(project) ?: return null)
}

override fun isEnabled(event: AnActionEvent): Boolean {
val usageView = event.getData(UsageView.USAGE_VIEW_KEY)
return usageView != null && usageView.usages.isNotEmpty()
Expand All @@ -163,13 +208,15 @@ class FindResultsModificationAction(
val config = dialog.showAndGetConfig()
return if (config != null) {
ModificationParams(
config.replacementText ?: ""
replacementText = config.replacementText ?: "",
autoApply = config.autoApply
)
} else null
}

data class ModificationParams(
val replacementText: String
val replacementText: String,
val autoApply: Boolean
)

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,52 +3,67 @@ package com.github.simiacryptus.aicoder.actions.find
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.DialogWrapper
import com.intellij.openapi.ui.ValidationInfo
import com.intellij.ui.components.JBLabel
import com.intellij.ui.components.JBScrollPane
import com.intellij.ui.components.JBTextArea
import com.intellij.util.ui.FormBuilder
import com.intellij.ui.dsl.builder.*
import com.intellij.ui.dsl.gridLayout.HorizontalAlign
import javax.swing.JComponent

class FindResultsModificationDialog(
project: Project,
matchCount: Int
) : DialogWrapper(project) {

private val replacementTextArea = JBTextArea().apply {
rows = 5
lineWrap = true
wrapStyleWord = true
text = "Please modify this code to:"
}
private val matchCountLabel = JBLabel("Found $matchCount matches")
private var replacementText = "Please modify this code to: "
private var autoApply = false

init {
title = "AI-Based Find Results Modification"
setOKButtonText("Modify Code")
init()
}

override fun createCenterPanel(): JComponent {
val formBuilder = FormBuilder.createFormBuilder()
formBuilder.addComponent(matchCountLabel)
formBuilder.addLabeledComponent("AI Instructions:", JBScrollPane(replacementTextArea))
return formBuilder.panel
return panel {
row("Modification Instructions:") {
textArea()
.bindText({ replacementText }, { replacementText = it })
.rows(5)
.align(Align.FILL)
.comment("Enter instructions for how you want the code to be modified")
.focused()
.apply {
component.lineWrap = true
component.wrapStyleWord = true
component.selectAll()
}
}.resizableRow()
row {
checkBox("Auto-apply changes")
.bindSelected({ autoApply }, { autoApply = it })
.comment("Automatically apply changes without manual confirmation")
}
}
}

override fun doValidate(): ValidationInfo? {
if (replacementTextArea.text.isBlank()) {
return ValidationInfo("Replacement text cannot be empty", replacementTextArea)
if (replacementText.isBlank()) {
return ValidationInfo("Please enter instructions for code modification")
}
if (replacementText.length < 10) {
return ValidationInfo("Please provide more detailed instructions")
}
return null
}

data class ConfigData(
val replacementText: String?
val replacementText: String?,
val autoApply: Boolean
)

fun showAndGetConfig(): ConfigData? {
if (showAndGet()) {
return ConfigData(
replacementText = replacementTextArea.text
replacementText = replacementText,
autoApply = autoApply
)
}
return null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,57 +134,22 @@ class MultiDiffChatAction : BaseAction() {
api: API
) {
try {
fun mainActor() = SimpleActor(
prompt = """
You are a helpful AI that helps people with coding.
You will be answering questions about the following code:
""".trimIndent() + codeSummary() + """
Response should use one or more code patches in diff format within ```diff code blocks.
Each diff should be preceded by a header that identifies the file being modified.
The diff format should use + for line additions, - for line deletions.
The diff should include 2 lines of context before and after every change.
Example:
Here are the patches:
### src/utils/exampleUtils.js
```diff
// Utility functions for example feature
const b = 2;
function exampleFunction() {
- return b + 1;
+ return b + 2;
}
```
### tests/exampleUtils.test.js
```diff
// Unit tests for exampleUtils
const assert = require('assert');
const { exampleFunction } = require('../src/utils/exampleUtils');
describe('exampleFunction', () => {
- it('should return 3', () => {
+ it('should return 4', () => {
assert.equal(exampleFunction(), 3);
});
});
```
If needed, new files can be created by using code blocks labeled with the filename in the same manner.
""".trimIndent(),
model = AppSettingsState.instance.smartModel.chatModel()
)
fun mainActor(): SimpleActor {
return SimpleActor(
prompt = """
You are a helpful AI that helps people with coding.
You will be answering questions about the following code:
""".trimIndent() + codeSummary() + patchEditorPrompt,
model = AppSettingsState.instance.smartModel.chatModel()
)
}

val settings = getSettings(session, user) ?: Settings()
if (api is ChatClient) api.budget = settings.budget ?: 2.00

val task = ui.newTask()
// Add progress indication
task.add("Processing request...")

val api = (api as ChatClient).getChildClient().apply {
Expand Down Expand Up @@ -261,6 +226,42 @@ class MultiDiffChatAction : BaseAction() {

companion object {
private val log = LoggerFactory.getLogger(MultiDiffChatAction::class.java)
val patchEditorPrompt = """
Response should use one or more code patches in diff format within ```diff code blocks.
Each diff should be preceded by a header that identifies the file being modified.
The diff format should use + for line additions, - for line deletions.
The diff should include 2 lines of context before and after every change.
Example:
Here are the patches:
### src/utils/exampleUtils.js
```diff
// Utility functions for example feature
const b = 2;
function exampleFunction() {
- return b + 1;
+ return b + 2;
}
```
### tests/exampleUtils.test.js
```diff
// Unit tests for exampleUtils
const assert = require('assert');
const { exampleFunction } = require('../src/utils/exampleUtils');
describe('exampleFunction', () => {
- it('should return 3', () => {
+ it('should return 4', () => {
assert.equal(exampleFunction(), 3);
});
});
```
If needed, new files can be created by using code blocks labeled with the filename in the same manner.
""".trimIndent()

}
}
Expand Down

0 comments on commit 6673ba4

Please sign in to comment.