diff --git a/CHANGELOG.md b/CHANGELOG.md index 922d0c35..f13379e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ ### Added - +## [1.1.1] + +### Added +- "Analogue File" action + +### Improved +- Various bug fixes + ## [1.1.0] ### Added diff --git a/build.gradle.kts b/build.gradle.kts index 99a23086..5f79b368 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,7 +5,6 @@ fun properties(key: String) = project.findProperty(key).toString() plugins { id("java") - id("scala") id("org.jetbrains.kotlin.jvm") version "1.7.21" id("org.jetbrains.intellij") version "1.13.3" id("org.jetbrains.changelog") version "2.0.0" @@ -24,31 +23,31 @@ repositories { val kotlin_version = "1.7.21" val jetty_version = "11.0.15" val slf4j_version = "2.0.5" -val skyenet_version = "1.0.7" +val skyenet_version = "1.0.8" dependencies { - implementation("com.simiacryptus:joe-penai:1.0.9") + implementation(group = "com.simiacryptus", name = "joe-penai", version = "1.0.10") - implementation("com.simiacryptus.skyenet:util:$skyenet_version") - implementation("com.simiacryptus.skyenet:core:$skyenet_version") - implementation("com.simiacryptus.skyenet:webui:$skyenet_version") + implementation(group = "com.simiacryptus.skyenet", name = "util", version = skyenet_version) + implementation(group = "com.simiacryptus.skyenet", name = "core", version = skyenet_version) + implementation(group = "com.simiacryptus.skyenet", name = "webui", version = skyenet_version) - implementation("org.eclipse.jetty:jetty-server:$jetty_version") - implementation("org.eclipse.jetty:jetty-servlet:$jetty_version") - implementation("org.eclipse.jetty:jetty-annotations:$jetty_version") - implementation("org.eclipse.jetty.websocket:websocket-jetty-server:$jetty_version") - implementation("org.eclipse.jetty.websocket:websocket-jetty-client:$jetty_version") - implementation("org.eclipse.jetty.websocket:websocket-servlet:$jetty_version") + implementation(group = "org.eclipse.jetty", name = "jetty-server", version = jetty_version) + implementation(group = "org.eclipse.jetty", name = "jetty-servlet", version = jetty_version) + implementation(group = "org.eclipse.jetty", name = "jetty-annotations", version = jetty_version) + implementation(group = "org.eclipse.jetty.websocket", name = "websocket-jetty-server", version = jetty_version) + implementation(group = "org.eclipse.jetty.websocket", name = "websocket-jetty-client", version = jetty_version) + implementation(group = "org.eclipse.jetty.websocket", name = "websocket-servlet", version = jetty_version) - implementation("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + implementation(group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version = kotlin_version) + implementation(group = "org.jetbrains.kotlin", name = "kotlin-reflect", version = kotlin_version) testImplementation(kotlin("script-runtime")) - implementation("org.slf4j:slf4j-api:$slf4j_version") + implementation(group = "org.slf4j", name = "slf4j-api", version = slf4j_version) - testImplementation("com.intellij.remoterobot:remote-robot:0.11.16") - testImplementation("com.intellij.remoterobot:remote-fixtures:0.11.16") - testImplementation("com.squareup.okhttp3:okhttp:3.14.9") + testImplementation(group = "com.intellij.remoterobot", name = "remote-robot", version = "0.11.16") + testImplementation(group = "com.intellij.remoterobot", name = "remote-fixtures", version = "0.11.16") + testImplementation(group = "com.squareup.okhttp3", name = "okhttp", version = "3.14.9") } diff --git a/gradle.properties b/gradle.properties index caa8c8c0..1bdbf805 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ pluginName = intellij-aicoder pluginRepositoryUrl = https://github.com/SimiaCryptus/intellij-aicoder # SemVer format -> https://semver.org -pluginVersion = 1.1.0 +pluginVersion = 1.1.1 # Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html pluginSinceBuild = 203 @@ -22,12 +22,6 @@ pluginUntilBuild = 231.* # IntelliJ IDEA Ultimate platformType = IU # https://mvnrepository.com/artifact/com.jetbrains.intellij.idea/ideaIU -#platformVersion = 2021.3.3 -#platformPlugins = com.intellij.java, org.intellij.scala:2021.3.22, Pythonid:213.7172.26, org.jetbrains.plugins.go:213.7172.6 - -#platformVersion = 2022.3.1 -#platformPlugins = com.intellij.java, org.intellij.scala:2022.3.16, Pythonid:223.8214.52, org.jetbrains.plugins.go:223.8214.52 - platformVersion = 2023.1.1 platformPlugins = com.intellij.java @@ -39,6 +33,9 @@ platformPlugins = com.intellij.java #platformType = RD #platformPlugins = JavaScript +#platformType = PY +#platformPlugins = Pythonid + # Gradle Releases -> https://github.com/gradle/gradle/releases gradleVersion = 8.1 diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/SoftwareProjectAI.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/SoftwareProjectAI.kt index 4e84f8a6..439a12d7 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/SoftwareProjectAI.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/SoftwareProjectAI.kt @@ -170,8 +170,7 @@ interface SoftwareProjectAI { } override fun validate(): Boolean { - if (file?.isBlank() != false) return false - return true + return file?.isBlank() == false // return super.validate() } } diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/BaseAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/BaseAction.kt index bb493d18..2c1d5e9c 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/BaseAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/BaseAction.kt @@ -1,6 +1,7 @@ package com.github.simiacryptus.aicoder.actions import com.github.simiacryptus.aicoder.util.UITools +import com.intellij.openapi.actionSystem.ActionUpdateThread import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent import com.simiacryptus.openai.OpenAIClient @@ -12,13 +13,15 @@ abstract class BaseAction( icon: Icon? = null, ) : AnAction(name, description, icon) { + //override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT + val api: OpenAIClient get() = UITools.api - override fun update(e: AnActionEvent) { - e.presentation.isEnabledAndVisible = isEnabled(e) - super.update(e) + override fun update(event: AnActionEvent) { + event.presentation.isEnabledAndVisible = isEnabled(event) + super.update(event) } - open fun isEnabled(e: AnActionEvent): Boolean = true + open fun isEnabled(event: AnActionEvent): Boolean = true } \ No newline at end of file diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/CommentsAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/CommentsAction.kt index 1ca55eee..77e8f848 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/CommentsAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/CommentsAction.kt @@ -39,7 +39,7 @@ class CommentsAction : BaseAction() { override fun actionPerformed(event: AnActionEvent) { - val editor = event.getRequiredData(CommonDataKeys.EDITOR) + val editor = event.getData(CommonDataKeys.EDITOR) ?: return val caretModel = editor.caretModel val primaryCaret = caretModel.primaryCaret val selectionStart = primaryCaret.selectionStart @@ -60,17 +60,16 @@ class CommentsAction : BaseAction() { ).code ?: "" } UITools.writeableFn(event) { - UITools.replaceString(editor.document, selectionStart, selectionEnd, newText!!) + UITools.replaceString(editor.document, selectionStart, selectionEnd, newText) } } } - override fun isEnabled(e: AnActionEvent): Boolean { + override fun isEnabled(event: AnActionEvent): Boolean { if (UITools.isSanctioned()) return false - if (!UITools.hasSelection(e)) return false - val computerLanguage = ComputerLanguage.getComputerLanguage(e) ?: return false - if (computerLanguage == ComputerLanguage.Text) return false - return true + if (!UITools.hasSelection(event)) return false + val computerLanguage = ComputerLanguage.getComputerLanguage(event) ?: return false + return computerLanguage != ComputerLanguage.Text } } \ No newline at end of file diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/ConvertFileToLanguage.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/ConvertFileToLanguage.kt index 63a788bc..b7034e0f 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/ConvertFileToLanguage.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/ConvertFileToLanguage.kt @@ -1,10 +1,10 @@ package com.github.simiacryptus.aicoder.actions.code +import com.github.simiacryptus.aicoder.actions.BaseAction import com.github.simiacryptus.aicoder.util.ComputerLanguage import com.github.simiacryptus.aicoder.util.UITools import com.github.simiacryptus.aicoder.util.UITools.getIndent import com.github.simiacryptus.aicoder.util.psi.PsiTranslationTree -import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.CommonDataKeys import com.intellij.openapi.command.WriteCommandAction @@ -14,14 +14,14 @@ import com.intellij.openapi.vfs.VirtualFile import java.io.IOException import java.util.concurrent.atomic.AtomicReference -class ConvertFileToLanguage(private val targetLanguage: ComputerLanguage) : AnAction( +class ConvertFileToLanguage(private val targetLanguage: ComputerLanguage) : BaseAction( targetLanguage.name ) { override fun actionPerformed(event: AnActionEvent) { val sourceLanguage = ComputerLanguage.getComputerLanguage(event) val indent = getIndent(event.getData(CommonDataKeys.CARET)) - val virtualFile = event.getRequiredData(CommonDataKeys.VIRTUAL_FILE) - val psiFile = event.getRequiredData(CommonDataKeys.PSI_FILE) + val virtualFile = event.getData(CommonDataKeys.VIRTUAL_FILE) ?: return + val psiFile = event.getData(CommonDataKeys.PSI_FILE) ?: return val project = event.project!! Thread { UITools.run(project, "Converting to " + targetLanguage.name) { diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/CustomEditAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/CustomEditAction.kt index aa5c8041..617d690a 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/CustomEditAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/CustomEditAction.kt @@ -48,7 +48,7 @@ open class CustomEditAction( ).create() override fun actionPerformed(event: AnActionEvent) { - val editor = event.getRequiredData(CommonDataKeys.EDITOR) + val editor = event.getData(CommonDataKeys.EDITOR) ?: return val caretModel = editor.caretModel val primaryCaret = caretModel.primaryCaret val selectionStart = primaryCaret.selectionStart @@ -75,7 +75,7 @@ open class CustomEditAction( UITools.writeableFn(event) { UITools.replaceString( editor.document, selectionStart, selectionEnd, - newText!! + newText ) } } @@ -85,11 +85,10 @@ open class CustomEditAction( open fun getInstruction(): String? = JOptionPane.showInputDialog(null, "Instruction:", "Edit Code", JOptionPane.QUESTION_MESSAGE) - override fun isEnabled(e: AnActionEvent): Boolean { + override fun isEnabled(event: AnActionEvent): Boolean { if (UITools.isSanctioned()) return false - if (!UITools.hasSelection(e)) return false - val computerLanguage = ComputerLanguage.getComputerLanguage(e) ?: return false - if (computerLanguage == ComputerLanguage.Text) return false - return true + if (!UITools.hasSelection(event)) return false + val computerLanguage = ComputerLanguage.getComputerLanguage(event) ?: return false + return computerLanguage != ComputerLanguage.Text } } \ No newline at end of file diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/DescribeAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/DescribeAction.kt index 1b1eaeeb..a68245b3 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/DescribeAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/DescribeAction.kt @@ -41,7 +41,7 @@ class DescribeAction : BaseAction() { override fun actionPerformed(event: AnActionEvent) { - val editor = event.getRequiredData(CommonDataKeys.EDITOR) + val editor = event.getData(CommonDataKeys.EDITOR) ?: return val caretModel = editor.caretModel val primaryCaret = caretModel.primaryCaret var selectionStart = primaryCaret.selectionStart @@ -67,7 +67,7 @@ class DescribeAction : BaseAction() { ) { proxy.describeCode( code = IndentedText.fromString(selectedText).textBlock.toString().trim(), - computerLanguage = language!!.name, + computerLanguage = language.name, humanLanguage = settings.humanLanguage, ).text ?: "" } @@ -94,11 +94,10 @@ class DescribeAction : BaseAction() { } } - override fun isEnabled(e: AnActionEvent): Boolean { + override fun isEnabled(event: AnActionEvent): Boolean { if (UITools.isSanctioned()) return false - val computerLanguage = ComputerLanguage.getComputerLanguage(e) ?: return false - if (computerLanguage == ComputerLanguage.Text) return false - return true + val computerLanguage = ComputerLanguage.getComputerLanguage(event) ?: return false + return computerLanguage != ComputerLanguage.Text } } \ No newline at end of file diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/DocAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/DocAction.kt index 33d159a8..95223690 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/DocAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/DocAction.kt @@ -6,7 +6,6 @@ import com.github.simiacryptus.aicoder.util.ComputerLanguage import com.github.simiacryptus.aicoder.util.IndentedText import com.github.simiacryptus.aicoder.util.UITools import com.github.simiacryptus.aicoder.util.UITools.insertString -import com.github.simiacryptus.aicoder.util.UITools.replaceString import com.github.simiacryptus.aicoder.util.psi.PsiUtil import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.CommonDataKeys @@ -75,7 +74,7 @@ class DocAction : BaseAction() { override fun actionPerformed(event: AnActionEvent) { val language = ComputerLanguage.getComputerLanguage(event) val caret = event.getData(CommonDataKeys.CARET) - val psiFile = event.getRequiredData(CommonDataKeys.PSI_FILE) + val psiFile = event.getData(CommonDataKeys.PSI_FILE) ?: return val smallestIntersectingMethod = PsiUtil.getSmallestIntersectingMajorCodeElement(psiFile, caret!!) ?: return @@ -83,8 +82,7 @@ class DocAction : BaseAction() { val code = smallestIntersectingMethod.text val indentedInput = IndentedText.fromString(code) val startOffset = smallestIntersectingMethod.textRange.startOffset - val endOffset = smallestIntersectingMethod.textRange.endOffset - val document = event.getRequiredData(CommonDataKeys.EDITOR).document + val document = (event.getData(CommonDataKeys.EDITOR) ?: return).document val outputHumanLanguage = settings.humanLanguage UITools.redoableTask(event) { @@ -113,7 +111,7 @@ class DocAction : BaseAction() { val computerLanguage = ComputerLanguage.getComputerLanguage(event) ?: return false if (computerLanguage == ComputerLanguage.Text) return false if (computerLanguage.docStyle.isEmpty()) return false - val psiFile = event.getRequiredData(CommonDataKeys.PSI_FILE) + val psiFile = event.getData(CommonDataKeys.PSI_FILE) ?: return false val caret = event.getData(CommonDataKeys.CARET) PsiUtil.getSmallestIntersectingMajorCodeElement(psiFile, caret!!) ?: return false return true diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/ImplementStubAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/ImplementStubAction.kt index 11d28af1..8e982f12 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/ImplementStubAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/ImplementStubAction.kt @@ -44,7 +44,7 @@ class ImplementStubAction : BaseAction() { override fun actionPerformed(event: AnActionEvent) { val caret = event.getData(CommonDataKeys.CARET) - val psiFile = event.getRequiredData(CommonDataKeys.PSI_FILE) + val psiFile = event.getData(CommonDataKeys.PSI_FILE) ?: return val smallestIntersectingMethod = getSmallestIntersectingMajorCodeElement(psiFile, caret!!) ?: return val computerLanguage = ComputerLanguage.getComputerLanguage(event) ?: return val settings = AppSettingsState.instance @@ -53,7 +53,7 @@ class ImplementStubAction : BaseAction() { //declaration = StringTools.stripPrefix(declaration.toString().trim(), PsiUtil.getDocComment(smallestIntersectingMethod).trim()); declaration = StringTools.stripSuffix(declaration.toString().trim(), getCode(smallestIntersectingMethod).trim()) declaration = declaration.toString().trim() - val document = event.getRequiredData(CommonDataKeys.EDITOR).document + val document = (event.getData(CommonDataKeys.EDITOR) ?: return).document val textRange = smallestIntersectingMethod.textRange val finalDeclaration = declaration val outputHumanLanguage = settings.humanLanguage diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/InsertImplementationAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/InsertImplementationAction.kt index 79c2c8ab..e6ae0516 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/InsertImplementationAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/InsertImplementationAction.kt @@ -41,10 +41,9 @@ class InsertImplementationAction : BaseAction() { val humanLanguage = AppSettingsState.instance.humanLanguage val computerLanguage = ComputerLanguage.getComputerLanguage(event) val psiClassContextActionParams = getPsiClassContextActionParams(event).get() - val editor = event.getRequiredData(CommonDataKeys.EDITOR) + val editor = event.getData(CommonDataKeys.EDITOR) ?: return val caretModel = editor.caretModel val primaryCaret = caretModel.primaryCaret - val settings = AppSettingsState.instance var instruct = psiClassContextActionParams.largestIntersectingComment.text.trim { it <= ' ' } if (primaryCaret.selectionEnd > primaryCaret.selectionStart) { val selectedText = Objects.requireNonNull(primaryCaret.selectedText) @@ -97,11 +96,11 @@ class InsertImplementationAction : BaseAction() { val largestIntersectingComment: PsiElement ) - override fun isEnabled(e: AnActionEvent): Boolean { + override fun isEnabled(event: AnActionEvent): Boolean { if (UITools.isSanctioned()) return false - val computerLanguage = ComputerLanguage.getComputerLanguage(e) ?: return false + val computerLanguage = ComputerLanguage.getComputerLanguage(event) ?: return false if (computerLanguage == ComputerLanguage.Text) return false - return getPsiClassContextActionParams(e).isPresent + return getPsiClassContextActionParams(event).isPresent } companion object { diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/PasteAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/PasteAction.kt index 3eacf38a..49e62e04 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/PasteAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/PasteAction.kt @@ -35,7 +35,7 @@ class PasteAction : BaseAction() { ).create() override fun actionPerformed(event: AnActionEvent) { - val editor = event.getRequiredData(CommonDataKeys.EDITOR) + val editor = event.getData(CommonDataKeys.EDITOR) ?: return val caretModel = editor.caretModel val primaryCaret = caretModel.primaryCaret val selectionStart = primaryCaret.selectionStart @@ -65,12 +65,12 @@ class PasteAction : BaseAction() { } } - override fun isEnabled(e: AnActionEvent): Boolean { + override fun isEnabled(event: AnActionEvent): Boolean { if (UITools.isSanctioned()) return false return if (CopyPasteManager.getInstance() .getContents(DataFlavor.stringFlavor) == null ) false else { - val computerLanguage = ComputerLanguage.getComputerLanguage(e) ?: return false + val computerLanguage = ComputerLanguage.getComputerLanguage(event) ?: return false computerLanguage != ComputerLanguage.Text } } diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/QuestionAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/QuestionAction.kt index 8bcd733b..8b1d1e0f 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/QuestionAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/QuestionAction.kt @@ -6,7 +6,6 @@ import com.github.simiacryptus.aicoder.util.ComputerLanguage import com.github.simiacryptus.aicoder.util.UITools import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.CommonDataKeys -import com.intellij.openapi.editor.Editor import com.simiacryptus.openai.proxy.ChatProxy import javax.swing.JOptionPane @@ -36,19 +35,17 @@ class QuestionAction : BaseAction() { deserializerRetries = 5, ).create() - override fun isEnabled(e: AnActionEvent): Boolean { + override fun isEnabled(event: AnActionEvent): Boolean { if (UITools.isSanctioned()) return false - val computerLanguage = ComputerLanguage.getComputerLanguage(e) ?: return false - if (computerLanguage == ComputerLanguage.Text) return false - return true + val computerLanguage = ComputerLanguage.getComputerLanguage(event) ?: return false + return computerLanguage != ComputerLanguage.Text } override fun actionPerformed(event: AnActionEvent) { - val editor = event.getRequiredData(CommonDataKeys.EDITOR) + val editor = event.getData(CommonDataKeys.EDITOR) ?: return val caretModel = editor.caretModel val primaryCaret = caretModel.primaryCaret var selectionStart = primaryCaret.selectionStart - var selectionEnd = primaryCaret.selectionEnd var selectedText = primaryCaret.selectedText val language = ComputerLanguage.getComputerLanguage(event)!! if (selectedText.isNullOrEmpty()) { @@ -58,7 +55,6 @@ class QuestionAction : BaseAction() { val lineEndOffset = document.getLineEndOffset(lineNumber) val currentLine = document.text.substring(lineStartOffset, lineEndOffset) selectionStart = lineStartOffset - selectionEnd = lineEndOffset selectedText = currentLine } diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/RecentCodeEditsAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/RecentCodeEditsAction.kt index 4afbc832..96a50c0e 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/RecentCodeEditsAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/RecentCodeEditsAction.kt @@ -31,7 +31,7 @@ class RecentCodeEditsAction : ActionGroup() { String.format("%d: %s", id, instruction) } children.add(object : CustomEditAction(text, instruction, null) { - override fun getInstruction(): String? { + override fun getInstruction(): String { return instruction } }) diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/RenameVariablesAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/RenameVariablesAction.kt index 7921b0f1..fa50c5ce 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/RenameVariablesAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/code/RenameVariablesAction.kt @@ -2,7 +2,8 @@ package com.github.simiacryptus.aicoder.actions.code import com.github.simiacryptus.aicoder.actions.BaseAction import com.github.simiacryptus.aicoder.config.AppSettingsState -import com.github.simiacryptus.aicoder.util.* +import com.github.simiacryptus.aicoder.util.ComputerLanguage +import com.github.simiacryptus.aicoder.util.UITools import com.github.simiacryptus.aicoder.util.UITools.replaceString import com.github.simiacryptus.aicoder.util.UITools.showCheckboxDialog import com.github.simiacryptus.aicoder.util.psi.PsiUtil @@ -12,7 +13,6 @@ import com.intellij.refactoring.suggested.endOffset import com.intellij.refactoring.suggested.startOffset import com.simiacryptus.openai.proxy.ChatProxy import org.jetbrains.annotations.NotNull -import org.jetbrains.annotations.Nullable class RenameVariablesAction : BaseAction() { @@ -41,17 +41,14 @@ class RenameVariablesAction : BaseAction() { override fun actionPerformed(@NotNull event: AnActionEvent) { - @NotNull val textEditor = event.getRequiredData(CommonDataKeys.EDITOR) + @NotNull val textEditor = event.getData(CommonDataKeys.EDITOR) ?: return @NotNull val caretModel = textEditor.caretModel @NotNull val mainCursor = caretModel.primaryCaret @NotNull val outputLanguage = AppSettingsState.instance.humanLanguage - val sourceFile = event.getRequiredData(CommonDataKeys.PSI_FILE) + val sourceFile = event.getData(CommonDataKeys.PSI_FILE) ?: return val codeElement = PsiUtil.getSmallestIntersectingMajorCodeElement(sourceFile, mainCursor) ?: throw IllegalStateException() @NotNull val programmingLanguage = ComputerLanguage.getComputerLanguage(event) - val appSettings = AppSettingsState.instance - @Nullable val textCursor = event.getData(CommonDataKeys.CARET) - val textIndent = UITools.getIndent(textCursor) UITools.redoableTask(event) { @@ -87,11 +84,10 @@ class RenameVariablesAction : BaseAction() { } } - override fun isEnabled(@NotNull e: AnActionEvent): Boolean { + override fun isEnabled(@NotNull event: AnActionEvent): Boolean { if (UITools.isSanctioned()) return false - val computerLanguage = ComputerLanguage.getComputerLanguage(e) ?: return false - if (computerLanguage == ComputerLanguage.Text) return false - return true + val computerLanguage = ComputerLanguage.getComputerLanguage(event) ?: return false + return computerLanguage != ComputerLanguage.Text } } diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/dev/AnalogueFileAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/dev/AnalogueFileAction.kt new file mode 100644 index 00000000..b9966a0b --- /dev/null +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/dev/AnalogueFileAction.kt @@ -0,0 +1,152 @@ +package com.github.simiacryptus.aicoder.actions.dev + +import com.github.simiacryptus.aicoder.actions.BaseAction +import com.github.simiacryptus.aicoder.config.AppSettingsState +import com.github.simiacryptus.aicoder.config.Name +import com.github.simiacryptus.aicoder.util.UITools +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.fileEditor.FileEditorManager +import com.intellij.openapi.vfs.LocalFileSystem +import com.intellij.openapi.vfs.VfsUtilCore.loadText +import com.simiacryptus.openai.ChatMessage +import com.simiacryptus.openai.ChatRequest +import com.simiacryptus.openai.OpenAIClient +import com.simiacryptus.skyenet.Brain +import org.apache.commons.io.IOUtils +import java.io.File +import javax.swing.JTextArea + +class AnalogueFileAction : BaseAction() { + + private data class ProjectFile( + var path: String? = "", + var code: String? = "", + ) + + @Suppress("UNUSED") + class SettingsUI { + @Name("Directive") + val directive = JTextArea( + """ + |Create test cases + |""".trimMargin().trim() + ) + } + + data class Settings( + var directive: String = "", + ) + + override fun actionPerformed(e: AnActionEvent) { + UITools.showDialog(e, SettingsUI::class.java, Settings::class.java, "Create Analogue File") { config -> + handleImplement(e, config) + } + } + + private fun handleImplement( + e: AnActionEvent, + config: Settings, + ) = Thread { + val virtualFile = UITools.getSelectedFile(e) ?: return@Thread + val project = e.project ?: return@Thread + UITools.run( + project, "Generating File", true + ) { + val analogue = try { + val directive = config.directive + val baseFile = ProjectFile( + path = virtualFile.canonicalPath!!, + code = IOUtils.toString(virtualFile.inputStream, "UTF-8") + ) + generateFile(baseFile, directive) + } finally { + if (it.isCanceled) throw InterruptedException() + } + + UITools.writeableFn(e) { + var path = analogue.path!! + var file = File(path) + if (file.exists()) { + val extension = path.split(".").takeLast(1) + val name = path.split(".").dropLast(1).joinToString(".") + val fileIndex = (1 until Int.MAX_VALUE).first { + !File("$name.$it.$extension").exists() + } + path = "$fileIndex.$it.$fileIndex" + file = File(path) + } + file.writeText(analogue.code!!) + LocalFileSystem.getInstance().findFileByIoFile(File(path).parentFile)?.refresh(false, true) + val generatedFile = LocalFileSystem.getInstance().findFileByIoFile(File(path)) + if (generatedFile == null) { + log.warn("Generated file not found: $path") + } else { + generatedFile.refresh(false, false) + FileEditorManager.getInstance(project).openFile(generatedFile, true) + } + Runnable { generatedFile?.delete(this@AnalogueFileAction) } + } + } + + }.start() + + private fun generateFile( + baseFile: ProjectFile, + directive: String, + ): ProjectFile { + val api = OpenAIClient( + key = AppSettingsState.instance.apiKey, + apiBase = AppSettingsState.instance.apiBase, + logLevel = AppSettingsState.instance.apiLogLevel + ) + val chatRequest = ChatRequest() + chatRequest.model = AppSettingsState.instance.model_chat + chatRequest.max_tokens = AppSettingsState.instance.maxTokens + chatRequest.temperature = AppSettingsState.instance.temperature + chatRequest.messages = arrayOf( + //language=TEXT + ChatMessage( + ChatMessage.Role.system, """ + |You will combine natural language instructions with a user provided code example to create a new file. + |Provide a new filename and the code to be written to the file. + |Paths should be relative to the project root and should not exist. + |Output the file path using the a line with the format "File: ". + |Output the file code in ``` code blocks labeled with language where appropriate. + """.trimMargin() + ), + //language=TEXT + ChatMessage( + ChatMessage.Role.user, """ + |Create a new file based on the following directive: $directive + | + |The file should be based on `${baseFile.path}` which contains the following code: + |``` + |${baseFile.code} + |``` + """.trimMargin() + ) + ) + val response = api.chat(chatRequest).response.get().toString() + val codeBlocks = Brain.extractCodeBlocks(response).filter { it.first != "text" } + require(codeBlocks.size == 1) { "Expected 1 code block, but found ${codeBlocks.size}" } + var outputPath = baseFile.path + val pathPattern = Regex("""(?s)\n?File(?:name)?: (?:'|`|")?([^\n]+)(?:'|`|")\n""") + if (pathPattern.containsMatchIn(response)) { + val match = pathPattern.find(response)!! + outputPath = match.groupValues[1] + } + return ProjectFile( + path = outputPath, + code = codeBlocks[0].second + ) + } + + override fun isEnabled(event: AnActionEvent): Boolean { + if (UITools.isSanctioned()) return false + return AppSettingsState.instance.devActions + } + + companion object { + private val log = org.slf4j.LoggerFactory.getLogger(AnalogueFileAction::class.java) + } +} diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/dev/CodeChatAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/dev/CodeChatAction.kt index 3564cff9..a51c1f81 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/dev/CodeChatAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/dev/CodeChatAction.kt @@ -1,3 +1,5 @@ +@file:Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "UNCHECKED_CAST") + package com.github.simiacryptus.aicoder.actions.dev import com.github.simiacryptus.aicoder.actions.BaseAction @@ -12,13 +14,14 @@ import com.simiacryptus.openai.ChatMessage import com.simiacryptus.openai.ChatRequest import com.simiacryptus.skyenet.Heart import com.simiacryptus.skyenet.body.ClasspathResource -import com.simiacryptus.skyenet.body.SkyenetSessionServer +import com.simiacryptus.skyenet.body.InterviewSession +import com.simiacryptus.skyenet.body.SkyenetCodingSession +import com.simiacryptus.skyenet.body.SkyenetCodingSessionServer import com.simiacryptus.skyenet.heart.WeakGroovyInterpreter -import com.simiacryptus.skyenet.util.AbbrevWhitelistYamlDescriber import org.eclipse.jetty.util.resource.Resource import org.jdesktop.swingx.JXButton import java.awt.Desktop -import java.util.Map +import java.io.File import java.util.function.Supplier class CodeChatAction : BaseAction() { @@ -28,36 +31,32 @@ class CodeChatAction : BaseAction() { port: Int, val language: String, val codeSelection: String, - ) : SkyenetSessionServer( + ) : SkyenetCodingSessionServer( applicationName = "Code Chat", - yamlDescriber = AbbrevWhitelistYamlDescriber( - "com.simiacryptus", - ), baseURL = "http://localhost:$port", - model = AppSettingsState.instance.model_chat + model = AppSettingsState.instance.model_chat, + apiKey = AppSettingsState.instance.apiKey, ) { val rootOperationID = (0..5).map { ('a'..'z').random() }.joinToString("") var rootMessageTrail: String = "" - override fun newSession(sessionId: String): SessionState { + override fun newSession(sessionId: String): SkyenetCodingSession { val newSession = CodeChatSession(sessionId) rootMessageTrail = """$rootOperationID,

Code:

$codeSelection
""" newSession.send(rootMessageTrail) return newSession } - open inner class CodeChatSession(sessionId: String) : SkyenetSession(sessionId) { + open inner class CodeChatSession(sessionId: String) : SkyenetCodingSession(sessionId, this@CodeChatServer) { override fun run(userMessage: String) { - val operationID = (0..5).map { ('a'..'z').random() }.joinToString("") - var messageTrail = """$operationID,""" - messageTrail += """
$userMessage
""" + var messageTrail = InterviewSession.initialText(userMessage) send("""$messageTrail
$spinner
""") messages += ChatMessage(ChatMessage.Role.user, userMessage) val response = api.chat(chatRequest).response.orNull()?.toString() ?: "???" messages += ChatMessage(ChatMessage.Role.assistant, response) messageTrail += """
$response
""" - send("""$messageTrail""") + send(messageTrail) } val messages = listOf( @@ -85,7 +84,7 @@ class CodeChatAction : BaseAction() { override val baseResource: Resource get() = ClasspathResource(javaClass.classLoader.getResource(resourceBase)) - override fun hands() = java.util.HashMap() as Map + override fun hands() = java.util.HashMap() as java.util.Map override fun toString(e: Throwable): String { return e.message ?: e.toString() @@ -103,7 +102,7 @@ class CodeChatAction : BaseAction() { } override fun actionPerformed(event: AnActionEvent) { - val editor = event.getRequiredData(CommonDataKeys.EDITOR) + val editor = event.getData(CommonDataKeys.EDITOR) ?: return val caretModel = editor.caretModel val primaryCaret = caretModel.primaryCaret val selectedText = primaryCaret.selectedText ?: editor.document.text @@ -150,10 +149,9 @@ class CodeChatAction : BaseAction() { }.start() } - override fun isEnabled(e: AnActionEvent): Boolean { - if (UITools.isSanctioned()) return false + override fun isEnabled(event: AnActionEvent): Boolean { + return !UITools.isSanctioned() //if (!AppSettingsState.instance.devActions) return false - return true } companion object { diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/dev/GenerateProjectAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/dev/GenerateProjectAction.kt index 1da11efe..f9d5ba0c 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/dev/GenerateProjectAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/dev/GenerateProjectAction.kt @@ -1,19 +1,18 @@ package com.github.simiacryptus.aicoder.actions.dev +import com.github.simiacryptus.aicoder.SoftwareProjectAI +import com.github.simiacryptus.aicoder.actions.BaseAction import com.github.simiacryptus.aicoder.config.AppSettingsState import com.github.simiacryptus.aicoder.config.Name import com.github.simiacryptus.aicoder.util.UITools -import com.github.simiacryptus.aicoder.SoftwareProjectAI -import com.simiacryptus.openai.proxy.ChatProxy -import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent -import com.simiacryptus.openai.OpenAIClient +import com.simiacryptus.openai.proxy.ChatProxy import java.io.File import javax.swing.JCheckBox import javax.swing.JTextArea import javax.swing.JTextField -class GenerateProjectAction : AnAction() { +class GenerateProjectAction : BaseAction() { override fun update(e: AnActionEvent) { e.presentation.isEnabledAndVisible = isEnabled() @@ -148,8 +147,7 @@ class GenerateProjectAction : AnAction() { private fun isEnabled(): Boolean { if (UITools.isSanctioned()) return false - if (!AppSettingsState.instance.devActions) return false - return true + return AppSettingsState.instance.devActions } companion object { diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/dev/GenerateStoryAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/dev/GenerateStoryAction.kt index 41b5364f..e7768390 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/dev/GenerateStoryAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/dev/GenerateStoryAction.kt @@ -4,11 +4,10 @@ import com.github.simiacryptus.aicoder.actions.BaseAction import com.github.simiacryptus.aicoder.config.AppSettingsState import com.github.simiacryptus.aicoder.config.Name import com.github.simiacryptus.aicoder.util.UITools -import com.simiacryptus.openai.proxy.ChatProxy -import com.simiacryptus.openai.proxy.Description -import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent import com.simiacryptus.openai.OpenAIClient +import com.simiacryptus.openai.proxy.ChatProxy +import com.simiacryptus.openai.proxy.Description import java.io.File import javax.swing.JTextArea import javax.swing.JTextField @@ -279,8 +278,7 @@ class GenerateStoryAction : BaseAction() { override fun isEnabled(event: AnActionEvent): Boolean { if (UITools.isSanctioned()) return false - if (!AppSettingsState.instance.devActions) return false - return true + return AppSettingsState.instance.devActions } companion object { diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/dev/LaunchSkyenetAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/dev/LaunchSkyenetAction.kt index 7122ab09..eeaa18e0 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/dev/LaunchSkyenetAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/dev/LaunchSkyenetAction.kt @@ -1,33 +1,29 @@ +@file:Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") + package com.github.simiacryptus.aicoder.actions.dev +import com.github.simiacryptus.aicoder.actions.BaseAction import com.github.simiacryptus.aicoder.config.AppSettingsState import com.github.simiacryptus.aicoder.util.UITools -import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile import com.intellij.util.ui.FormBuilder -import com.simiacryptus.openai.OpenAIClient import com.simiacryptus.openai.proxy.Description import com.simiacryptus.skyenet.Heart import com.simiacryptus.skyenet.OutputInterceptor import com.simiacryptus.skyenet.body.ClasspathResource import com.simiacryptus.skyenet.body.SessionServerUtil.asJava -import com.simiacryptus.skyenet.body.SkyenetSessionServer +import com.simiacryptus.skyenet.body.SkyenetCodingSessionServer import com.simiacryptus.skyenet.heart.WeakGroovyInterpreter -import com.simiacryptus.skyenet.util.AbbrevWhitelistYamlDescriber -import org.apache.commons.io.output.NullOutputStream -import org.eclipse.jetty.server.Server import org.eclipse.jetty.util.resource.Resource -import org.eclipse.jetty.webapp.WebAppContext import org.jdesktop.swingx.JXButton import java.awt.Desktop -import java.io.ByteArrayOutputStream -import java.io.PrintStream +import java.io.File import java.util.Map import java.util.function.Supplier -class LaunchSkyenetAction : AnAction() { +class LaunchSkyenetAction : BaseAction() { override fun update(e: AnActionEvent) { e.presentation.isEnabledAndVisible = isEnabled() @@ -44,15 +40,13 @@ class LaunchSkyenetAction : AnAction() { override fun actionPerformed(e: AnActionEvent) { // Random port from range 8000-9000 val port = (8000 + (Math.random() * 1000).toInt()) - val skyenet = object : SkyenetSessionServer( + val skyenet = object : SkyenetCodingSessionServer( applicationName = "IdeaAgent", - yamlDescriber = AbbrevWhitelistYamlDescriber( - "com.simiacryptus", - ), baseURL = "http://localhost:$port", - model = AppSettingsState.instance.model_chat + model = AppSettingsState.instance.model_chat, + apiKey = AppSettingsState.instance.apiKey ) { - override val baseResource: Resource? + override val baseResource: Resource get() = ClasspathResource(javaClass.classLoader.getResource(resourceBase)) override fun hands() = Map.of( @@ -82,7 +76,7 @@ class LaunchSkyenetAction : AnAction() { return e.message ?: e.toString() } - override fun heart(hands: java.util.Map): Heart = object : WeakGroovyInterpreter(hands) { + override fun heart(hands: Map): Heart = object : WeakGroovyInterpreter(hands) { override fun wrapExecution(fn: Supplier): T? { return UITools.run( e.project, "Running Script", false @@ -123,9 +117,8 @@ class LaunchSkyenetAction : AnAction() { } private fun isEnabled(): Boolean { - if (UITools.isSanctioned()) return false + return !UITools.isSanctioned() //if (!AppSettingsState.instance.devActions) return false - return true } companion object { diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/dev/PrintTreeAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/dev/PrintTreeAction.kt index 8f74267a..9347f9af 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/dev/PrintTreeAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/dev/PrintTreeAction.kt @@ -1,9 +1,9 @@ package com.github.simiacryptus.aicoder.actions.dev +import com.github.simiacryptus.aicoder.actions.BaseAction import com.github.simiacryptus.aicoder.config.AppSettingsState import com.github.simiacryptus.aicoder.util.UITools import com.github.simiacryptus.aicoder.util.psi.PsiUtil -import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.diagnostic.Logger @@ -14,7 +14,7 @@ import com.intellij.openapi.diagnostic.Logger * Finally, select the "PrintTreeAction" action from the editor context menu. * This will print the tree structure of the file to the log. */ -class PrintTreeAction : AnAction() { +class PrintTreeAction : BaseAction() { override fun update(e: AnActionEvent) { e.presentation.isEnabledAndVisible = isEnabled(e) super.update(e) diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/AppendAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/AppendAction.kt index 356ce313..968533ac 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/AppendAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/AppendAction.kt @@ -5,6 +5,7 @@ import com.github.simiacryptus.aicoder.config.AppSettingsState import com.github.simiacryptus.aicoder.util.UITools import com.github.simiacryptus.aicoder.util.UITools.hasSelection import com.github.simiacryptus.aicoder.util.UITools.insertString +import com.intellij.openapi.actionSystem.ActionUpdateThread import com.simiacryptus.openai.ChatMessage import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.CommonDataKeys @@ -32,7 +33,7 @@ class AppendAction : BaseAction() { before.toString() ) ) - val document = event.getRequiredData(CommonDataKeys.EDITOR).document + val document = (event.getData(CommonDataKeys.EDITOR) ?: return).document val selectionEnd = caret!!.selectionEnd UITools.redoableTask(event) { val newText = UITools.run( @@ -46,9 +47,9 @@ class AppendAction : BaseAction() { } } @Suppress("unused") - override fun isEnabled(e: AnActionEvent): Boolean { + override fun isEnabled(event: AnActionEvent): Boolean { if (UITools.isSanctioned()) return false - return hasSelection(e) + return hasSelection(event) } } diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/DictationAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/DictationAction.kt index 6f46aeeb..41aa488e 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/DictationAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/DictationAction.kt @@ -3,6 +3,7 @@ package com.github.simiacryptus.aicoder.actions.generic import com.github.simiacryptus.aicoder.actions.BaseAction import com.simiacryptus.util.AudioRecorder import com.github.simiacryptus.aicoder.util.UITools +import com.intellij.openapi.actionSystem.ActionUpdateThread import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.CommonDataKeys import com.intellij.openapi.actionSystem.PlatformDataKeys @@ -11,13 +12,16 @@ import com.intellij.openapi.diagnostic.Logger import com.simiacryptus.util.LookbackLoudnessWindowBuffer import java.util.* import java.util.concurrent.ConcurrentLinkedDeque +import java.util.concurrent.Executors +import java.util.concurrent.Future +import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicInteger import javax.sound.sampled.AudioSystem +import javax.sound.sampled.TargetDataLine import javax.swing.JFrame import javax.swing.JLabel class DictationAction : BaseAction() { - override fun actionPerformed(event: AnActionEvent) { val continueFn = statusDialog(event)::isVisible @@ -43,7 +47,7 @@ class DictationAction : BaseAction() { log.warn("Audio processing thread complete") }, "dictation-audio-processor").start() - val caretModel = event.getRequiredData(CommonDataKeys.EDITOR).caretModel + val caretModel = (event.getData(CommonDataKeys.EDITOR) ?: return).caretModel val primaryCaret = caretModel.primaryCaret val dictationPump = if (primaryCaret.hasSelection()) { DictationPump(event, wavBuffer, continueFn, primaryCaret.selectionEnd, primaryCaret.selectedText ?: "") @@ -88,7 +92,7 @@ class DictationAction : BaseAction() { ) prompt = newPrompt WriteCommandAction.runWriteCommandAction(event.project) { - val editor = event.getRequiredData(CommonDataKeys.EDITOR) + val editor = event.getData(CommonDataKeys.EDITOR) ?: return@runWriteCommandAction editor.document.insertString(offset.getAndAdd(text.length), text) } } @@ -111,16 +115,23 @@ class DictationAction : BaseAction() { override fun isEnabled(event: AnActionEvent): Boolean { if (UITools.isSanctioned()) return false - try { - AudioSystem.getTargetDataLine(AudioRecorder.audioFormat) + return try { + null != targetDataLine.get(50, TimeUnit.MILLISECONDS) } catch (e: Exception) { - return false + false } - return true } companion object { val log = Logger.getInstance(DictationAction::class.java) + + val pool = Executors.newFixedThreadPool(1) + + val targetDataLine: Future by lazy { + pool.submit { + AudioSystem.getTargetDataLine(AudioRecorder.audioFormat) + } + } } } \ No newline at end of file diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/RedoLast.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/RedoLast.kt index cb5fbca3..f469445f 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/RedoLast.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/RedoLast.kt @@ -1,8 +1,9 @@ package com.github.simiacryptus.aicoder.actions.generic +import com.github.simiacryptus.aicoder.actions.BaseAction import com.github.simiacryptus.aicoder.util.UITools import com.github.simiacryptus.aicoder.util.UITools.retry -import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.ActionUpdateThread import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.CommonDataKeys @@ -11,7 +12,7 @@ import com.intellij.openapi.actionSystem.CommonDataKeys * To use this action, open the editor and select the RedoLast action from the editor context menu. * This will redo the last action that was performed in the editor. */ -class RedoLast : AnAction() { +class RedoLast : BaseAction() { override fun update(e: AnActionEvent) { e.presentation.isEnabledAndVisible = isEnabled(e) super.update(e) diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/ReplaceOptionsAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/ReplaceOptionsAction.kt index 3b6a02ad..a0d73d67 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/ReplaceOptionsAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/ReplaceOptionsAction.kt @@ -4,6 +4,7 @@ import com.github.simiacryptus.aicoder.actions.BaseAction import com.github.simiacryptus.aicoder.config.AppSettingsState import com.github.simiacryptus.aicoder.util.UITools import com.github.simiacryptus.aicoder.util.UITools.showRadioButtonDialog +import com.intellij.openapi.actionSystem.ActionUpdateThread import com.simiacryptus.util.StringTools import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.CommonDataKeys @@ -14,7 +15,6 @@ import kotlin.math.ln import kotlin.math.pow class ReplaceOptionsAction : BaseAction() { - interface VirtualAPI { fun suggestText(prefix: String, example: String, suffix: String): Suggestions data class Suggestions( @@ -67,8 +67,8 @@ class ReplaceOptionsAction : BaseAction() { } } - override fun isEnabled(e: AnActionEvent): Boolean { + override fun isEnabled(event: AnActionEvent): Boolean { if (UITools.isSanctioned()) return false - return UITools.hasSelection(e) + return UITools.hasSelection(event) } } \ No newline at end of file diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/markdown/MarkdownImplementAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/markdown/MarkdownImplementAction.kt index c7b91460..b68f40e9 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/markdown/MarkdownImplementAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/markdown/MarkdownImplementAction.kt @@ -43,7 +43,7 @@ class MarkdownImplementAction(private val language: String) : BaseAction( val caret = event.getData(CommonDataKeys.CARET) val selectedText = caret!!.selectedText ?: return val endOffset = caret.selectionEnd - val document = event.getRequiredData(CommonDataKeys.EDITOR).document + val document = (event.getData(CommonDataKeys.EDITOR) ?: return).document UITools.redoableTask(event) { val code = UITools.run( @@ -64,10 +64,10 @@ class MarkdownImplementAction(private val language: String) : BaseAction( } } - override fun isEnabled(e: AnActionEvent): Boolean { + override fun isEnabled(event: AnActionEvent): Boolean { if (UITools.isSanctioned()) return false - val computerLanguage = ComputerLanguage.getComputerLanguage(e) ?: return false + val computerLanguage = ComputerLanguage.getComputerLanguage(event) ?: return false if (ComputerLanguage.Markdown != computerLanguage) return false - return hasSelection(e) + return hasSelection(event) } } \ No newline at end of file diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/markdown/MarkdownImplementActionGroup.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/markdown/MarkdownImplementActionGroup.kt index 3f46ba8c..db09e79a 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/markdown/MarkdownImplementActionGroup.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/markdown/MarkdownImplementActionGroup.kt @@ -60,6 +60,7 @@ class MarkdownImplementActionGroup : ActionGroup() { override fun getChildren(e: AnActionEvent?): Array { if(null == e) return arrayOf() val computerLanguage = ComputerLanguage.getComputerLanguage(e) + if (null == computerLanguage) return arrayOf() val actions = ArrayList() for (language in markdownLanguages) { if (computerLanguage!!.name == language) continue diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/markdown/MarkdownListAction.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/markdown/MarkdownListAction.kt index bec88aa2..8cf836c3 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/actions/markdown/MarkdownListAction.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/actions/markdown/MarkdownListAction.kt @@ -66,7 +66,7 @@ class MarkdownListAction : BaseAction() { val indent = getIndent(caret) val endOffset = list.textRange.endOffset val bulletTypes = listOf("- [ ] ", "- ", "* ") - val document = event.getRequiredData(CommonDataKeys.EDITOR).document + val document = (event.getData(CommonDataKeys.EDITOR) ?: return).document val rawItems = items.map(CharSequence::trim).map { val bulletType = bulletTypes.find(it::startsWith) if (null != bulletType) StringTools.stripPrefix(it, bulletType).toString() diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/config/AppSettingsComponent.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/config/AppSettingsComponent.kt index 03231411..54e5d092 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/config/AppSettingsComponent.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/config/AppSettingsComponent.kt @@ -9,7 +9,6 @@ import com.intellij.ui.components.JBCheckBox import com.intellij.ui.components.JBPasswordField import com.intellij.ui.components.JBTextField import java.awt.event.ActionEvent -import java.util.* import javax.swing.AbstractAction import javax.swing.JButton import javax.swing.JComponent diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/config/AppSettingsConfigurable.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/config/AppSettingsConfigurable.kt index 4b05198d..74cceab2 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/config/AppSettingsConfigurable.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/config/AppSettingsConfigurable.kt @@ -2,7 +2,6 @@ package com.github.simiacryptus.aicoder.config import com.github.simiacryptus.aicoder.util.UITools import com.intellij.openapi.options.Configurable -import org.jetbrains.annotations.Nls import java.util.* import javax.swing.JComponent import javax.swing.JPanel diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/config/Name.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/config/Name.kt index 7a74dc73..3db50deb 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/config/Name.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/config/Name.kt @@ -1,7 +1,5 @@ package com.github.simiacryptus.aicoder.config -import java.util.* - @Retention(AnnotationRetention.RUNTIME) annotation class Name(val value: String) diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/util/BlockComment.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/util/BlockComment.kt index eaddcc46..1d54cc58 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/util/BlockComment.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/util/BlockComment.kt @@ -1,3 +1,5 @@ +@file:Suppress("NAME_SHADOWING") + package com.github.simiacryptus.aicoder.util import com.github.simiacryptus.aicoder.util.TextBlock.Companion.DELIMITER diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/util/ComputerLanguage.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/util/ComputerLanguage.kt index 547669ad..a2acf64f 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/util/ComputerLanguage.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/util/ComputerLanguage.kt @@ -465,9 +465,8 @@ enum class ComputerLanguage(configuration: Configuration) { }.findAny().orElse(null) } - fun getComputerLanguage(e: AnActionEvent): ComputerLanguage? { - val file = e.getData(CommonDataKeys.VIRTUAL_FILE) - ?: return null + fun getComputerLanguage(e: AnActionEvent?): ComputerLanguage? { + val file = e?.getData(CommonDataKeys.VIRTUAL_FILE) ?: return null val extension = if (file.extension != null) file.extension!!.lowercase(Locale.getDefault()) else "" return findByExtension(extension) } diff --git a/src/main/kotlin/com/github/simiacryptus/aicoder/util/UITools.kt b/src/main/kotlin/com/github/simiacryptus/aicoder/util/UITools.kt index b76092e4..0e6f1041 100644 --- a/src/main/kotlin/com/github/simiacryptus/aicoder/util/UITools.kt +++ b/src/main/kotlin/com/github/simiacryptus/aicoder/util/UITools.kt @@ -1,4 +1,4 @@ -@file:Suppress("UNNECESSARY_SAFE_CALL") +@file:Suppress("UNNECESSARY_SAFE_CALL", "UNCHECKED_CAST") package com.github.simiacryptus.aicoder.util @@ -66,16 +66,22 @@ object UITools { ) { Futures.addCallback(pool.submit { request.get() - }, object : FutureCallback { - override fun onSuccess(undo: Runnable) { - val document = event.getRequiredData(CommonDataKeys.EDITOR).document - retry[document] = getRetry(event, request, undo) - } + }, futureCallback(event, request), pool) + } - override fun onFailure(t: Throwable) { - handle(t) - } - }, pool) + fun futureCallback( + event: AnActionEvent, + request: Supplier, + ) = object : FutureCallback { + override fun onSuccess(undo: Runnable) { + val requiredData = event.getData(CommonDataKeys.EDITOR) ?: return + val document = requiredData.document + retry[document] = getRetry(event, request, undo) + } + + override fun onFailure(t: Throwable) { + handle(t) + } } fun getRetry( @@ -89,16 +95,7 @@ object UITools { WriteCommandAction.runWriteCommandAction(event.project) { undo?.run() } request.get() }, - object : FutureCallback { - override fun onSuccess(nextUndo: Runnable) { - val document = event.getRequiredData(CommonDataKeys.EDITOR).document - retry[document] = getRetry(event, request, nextUndo) - } - - override fun onFailure(t: Throwable) { - handle(t) - } - }, + futureCallback(event, request), pool ) } @@ -773,8 +770,7 @@ object UITools { // ISO 3166 - Uzbekistan if (locale.country.compareTo("UZ", true) == 0) return false // ISO 3166 - Mongolia - if (locale.country.compareTo("MN", true) == 0) return false - return true + return locale.country.compareTo("MN", true) != 0 } return false } @@ -791,6 +787,7 @@ object UITools { e: AnActionEvent, uiClass: Class, configClass: Class, + title: String = "Generate Project", onComplete: (C) -> Unit, ) { val project = e.project @@ -799,10 +796,10 @@ object UITools { val dialog = object : DialogWrapper(project) { init { this.init() - this.title = "Generate Project" + this.title = title this.setOKButtonText("Generate") this.setCancelButtonText("Cancel") - this.setResizable(true) + this.isResizable = true //this.setPreferredFocusedComponent(this) //this.setContent(this) } @@ -834,6 +831,21 @@ object UITools { } return project?.baseDir } + fun getSelectedFile(e: AnActionEvent): VirtualFile? { + val dataContext = e.dataContext + val data = PlatformDataKeys.VIRTUAL_FILE.getData(dataContext) + if (data != null && !data.isDirectory) { + return data + } + val editor = PlatformDataKeys.EDITOR.getData(dataContext) + if (editor != null) { + val file = FileDocumentManager.getInstance().getFile(editor.document) + if (file != null) { + return file + } + } + return null + } fun isInterruptedException(e: Throwable?): Boolean { if (e is InterruptedException) return true @@ -931,7 +943,7 @@ object UITools { fun checkApiKey(k: String = AppSettingsState.instance.apiKey): String { var key = k if (key.isEmpty()) { - synchronized(OpenAIClient.javaClass) { + synchronized(OpenAIClient::class.java) { key = AppSettingsState.instance.apiKey if (key.isEmpty()) { key = queryAPIKey()!!.toString() @@ -961,7 +973,7 @@ object UITools { fun map( moderateAsync: ListenableFuture, o: com.google.common.base.Function, - ): ListenableFuture = Futures.transform(moderateAsync, o, UITools.pool) + ): ListenableFuture = Futures.transform(moderateAsync, o, pool) fun filterStringResult( indent: CharSequence = "", diff --git a/src/main/kotlin/com/simiacryptus/skyenet/heart/WeakGroovyInterpreter.kt b/src/main/kotlin/com/simiacryptus/skyenet/heart/WeakGroovyInterpreter.kt index aa62fca8..9baddd23 100644 --- a/src/main/kotlin/com/simiacryptus/skyenet/heart/WeakGroovyInterpreter.kt +++ b/src/main/kotlin/com/simiacryptus/skyenet/heart/WeakGroovyInterpreter.kt @@ -1,3 +1,5 @@ +@file:Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") + package com.simiacryptus.skyenet.heart import com.simiacryptus.skyenet.Heart diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 98eaf6b2..834b83a6 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -140,6 +140,16 @@ + + + + + diff --git a/src/test/kotlin/com/github/simiacryptus/aicoder/DocGen.kt b/src/test/kotlin/com/github/simiacryptus/aicoder/DocGen.kt index 44dc9577..884b4362 100644 --- a/src/test/kotlin/com/github/simiacryptus/aicoder/DocGen.kt +++ b/src/test/kotlin/com/github/simiacryptus/aicoder/DocGen.kt @@ -6,6 +6,7 @@ import org.junit.Test import java.io.File import java.io.FileOutputStream import java.io.PrintWriter +import java.util.* /** * See Also: @@ -114,11 +115,11 @@ class DocGen { return shortcut.trim() .split(" ") .map { it.substringAfter("=").replace("\"", "").trim() } - .joinToString(" + ") { "${it.capitalize()}" } + .joinToString(" + ") { "${it.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }}" } } private fun loadActions(input: File): Array> { - val pluginXml = FileUtils.readFileToString(input, "UTF-8"); + val pluginXml = FileUtils.readFileToString(input, "UTF-8") return (pluginXml.split("") } + pluginXml.split("") }) .filter { it.contains("