diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8b16c74e..6c1c1d66 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,13 @@
## [Unreleased]
+## [0.1.9]
+
+### Added
+- Dev mode now adds a API request preview/testing UI for each request
+- Various UI polish
+- Refactored code so Actions are independent components
+
## [0.1.8]
### Added
diff --git a/build.gradle.kts b/build.gradle.kts
index da813e83..40bf5e59 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -28,6 +28,7 @@ repositories {
// Set the JVM language level used to build project. Use Java 11 for 2020.3+, and Java 17 for 2022.2+.
kotlin {
+// jvmToolchain(17)
jvmToolchain(11)
}
diff --git a/gradle.properties b/gradle.properties
index c375c43e..51d9e425 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -4,7 +4,7 @@ pluginGroup = com.github.simiacryptus
pluginName = intellij-aicoder
pluginRepositoryUrl = https://github.com/SimiaCryptus/intellij-aicoder
# SemVer format -> https://semver.org
-pluginVersion = 0.1.8
+pluginVersion = 0.1.9
# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
pluginSinceBuild = 203
@@ -13,6 +13,7 @@ pluginUntilBuild = 223.*
# IntelliJ Platform Properties -> https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#configuration-intellij-extension
platformType = IC
platformVersion = 2021.3.3
+#platformVersion = 2022.3.1
# Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html
# Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22
diff --git a/src/main/java/com/github/simiacryptus/aicoder/EditorMenu.java b/src/main/java/com/github/simiacryptus/aicoder/EditorMenu.java
deleted file mode 100644
index cc430afa..00000000
--- a/src/main/java/com/github/simiacryptus/aicoder/EditorMenu.java
+++ /dev/null
@@ -1,688 +0,0 @@
-package com.github.simiacryptus.aicoder;
-
-import com.github.simiacryptus.aicoder.config.AppSettingsState;
-import com.github.simiacryptus.aicoder.openai.CompletionRequest;
-import com.github.simiacryptus.aicoder.openai.ModerationException;
-import com.github.simiacryptus.aicoder.psi.PsiClassContext;
-import com.github.simiacryptus.aicoder.psi.PsiMarkdownContext;
-import com.github.simiacryptus.aicoder.psi.PsiUtil;
-import com.github.simiacryptus.aicoder.util.*;
-import com.intellij.core.CoreBundle;
-import com.intellij.openapi.actionSystem.ActionGroup;
-import com.intellij.openapi.actionSystem.AnAction;
-import com.intellij.openapi.actionSystem.AnActionEvent;
-import com.intellij.openapi.actionSystem.CommonDataKeys;
-import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.editor.Caret;
-import com.intellij.openapi.editor.CaretModel;
-import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.ide.CopyPasteManager;
-import com.intellij.openapi.util.TextRange;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.psi.PsiElement;
-import com.intellij.psi.PsiFile;
-import org.jetbrains.annotations.Nls;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import javax.swing.*;
-import java.awt.datatransfer.DataFlavor;
-import java.io.IOException;
-import java.util.*;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-
-public class EditorMenu extends ActionGroup {
-
- private static final Logger log = Logger.getInstance(EditorMenu.class);
- public static final @NotNull
- @Nls String DEFAULT_ACTION_MESSAGE = CoreBundle.message("command.name.undefined");
-
- /**
- * This method is used to get the children of the action.
- *
- * This Java code is an override of the getChildren() method. It is used to create an array of AnAction objects that will be used to create a context menu for a file.
- * The code first gets the extension of the file and sets the computer language and comment line prefix based on the extension.
- * It then checks if the copy/paste manager has data flavors available and calls the pasteAction() method if it does.
- * It then checks the extension and calls the docAction() method if it is either Java or Scala.
- * It then checks if there is a selection and calls the customTranslation(), autoImplementationAction(), describeAction(), and standardCodeActions() methods if there is.
- * Finally, it returns an array of AnAction objects.
- *
- * @param e AnActionEvent object that contains the necessary data to perform the action.
- * @return An array of AnAction objects that are the children of the action.
- */
- @Override
- public AnAction @NotNull [] getChildren(@NotNull AnActionEvent e) {
- AppSettingsState settings = AppSettingsState.getInstance();
- String inputHumanLanguage = settings.humanLanguage;
- String outputHumanLanguage = settings.humanLanguage;
- VirtualFile file = e.getData(CommonDataKeys.VIRTUAL_FILE);
- if (file == null) return new AnAction[]{};
- String extension = file.getExtension() != null ? file.getExtension().toLowerCase() : "";
- ArrayList children = new ArrayList<>();
- ComputerLanguage language = ComputerLanguage.findByExtension(extension);
-
- Caret caret = e.getData(CommonDataKeys.CARET);
-
- if (null != caret) {
- addIfNotNull(children, redoLast());
- if (!caret.hasSelection()) {
- children.add(genericInsert());
- } else {
- children.add(genericAppend());
- }
- }
-
-
- if (language != null) {
-
- addIfNotNull(children, rewordCommentAction(e, language, inputHumanLanguage));
-
- if (settings.devActions) {
- addIfNotNull(children, printTreeAction(e));
- }
-
- if (CopyPasteManager.getInstance().areDataFlavorsAvailable(DataFlavor.stringFlavor)) {
- children.add(pasteAction(language.name()));
- }
-
- if (language.docStyle.length() > 0) {
- children.add(docAction(extension, language));
- }
-
- if (language == ComputerLanguage.Markdown) {
- addIfNotNull(children, markdownListAction(e));
- addIfNotNull(children, markdownNewTableRowsAction(e));
- addIfNotNull(children, markdownNewTableColsAction(e));
- addIfNotNull(children, markdownNewTableColAction(e));
- }
-
- if (null != caret) {
- if (caret.hasSelection()) {
- children.add(customEdit(language.name()));
- children.add(recentEdits(language.name()));
- switch (language) {
- case Markdown:
- addIfNotNull(children, markdownContextAction(e, inputHumanLanguage));
- break;
- default:
- addIfNotNull(children, psiClassContextAction(e, language, inputHumanLanguage));
- break;
- }
- children.add(describeAction(outputHumanLanguage, language));
- children.add(addCodeCommentsAction(outputHumanLanguage, language));
- children.add(fromHumanLanguageAction(inputHumanLanguage, language));
- children.add(toHumanLanguageAction(outputHumanLanguage, language));
- }
- }
- }
-
- return children.toArray(AnAction[]::new);
- }
-
- @NotNull
- protected AnAction toHumanLanguageAction(String outputHumanLanguage, ComputerLanguage language) {
- String computerLanguage = language.name();
- String description = String.format("Describe %s -> %s", outputHumanLanguage, computerLanguage);
- return TextReplacementAction.create("_To " + outputHumanLanguage, description, null,
- (event, string) -> {
- AppSettingsState settings = AppSettingsState.getInstance();
- return settings.createTranslationRequest()
- .setInstruction(UITools.getInstruction("Describe this code"))
- .setInputText(string)
- .setInputType(computerLanguage)
- .setInputAttribute("type", "input")
- .setOutputType(outputHumanLanguage.toLowerCase())
- .setOutputAttrute("type", "output")
- .setOutputAttrute("style", settings.style)
- .buildCompletionRequest();
- });
- }
-
- @NotNull
- protected AnAction fromHumanLanguageAction(String inputHumanLanguage, ComputerLanguage language) {
- String computerLanguage = language.name();
- String description = String.format("Implement %s -> %s", inputHumanLanguage, computerLanguage);
- return TextReplacementAction.create("_From " + inputHumanLanguage, description, null, (event, string) ->
- AppSettingsState.getInstance().createTranslationRequest()
- .setInputType(inputHumanLanguage.toLowerCase())
- .setOutputType(computerLanguage)
- .setInstruction("Implement this specification")
- .setInputAttribute("type", "input")
- .setOutputAttrute("type", "output")
- .setInputText(string)
- .buildCompletionRequest());
- }
-
- @NotNull
- protected AnAction addCodeCommentsAction(CharSequence outputHumanLanguage, ComputerLanguage language) {
- String computerLanguage = language.name();
- return TextReplacementAction.create("Add Code _Comments", "Add Code Comments", null, (event, string) -> {
- AppSettingsState settings = AppSettingsState.getInstance();
- return settings.createTranslationRequest()
- .setInputType(computerLanguage)
- .setOutputType(computerLanguage)
- .setInstruction(UITools.getInstruction("Rewrite to include detailed " + outputHumanLanguage + " code comments for every line"))
- .setInputAttribute("type", "uncommented")
- .setOutputAttrute("type", "commented")
- .setOutputAttrute("style", settings.style)
- .setInputText(string)
- .buildCompletionRequest();
- });
- }
-
- @NotNull
- protected AnAction describeAction(String outputHumanLanguage, ComputerLanguage language) {
- return TextReplacementAction.create("_Describe Code and Prepend Comment", "Add JavaDoc Comments", null, new TextReplacementAction.ActionTextEditorFunction() {
- @Override
- public CompletionRequest apply(AnActionEvent event, String inputString) throws IOException, ModerationException {
- AppSettingsState settings = AppSettingsState.getInstance();
- return settings.createTranslationRequest()
- .setInputType(language.name())
- .setOutputType(outputHumanLanguage)
- .setInstruction(UITools.getInstruction("Explain this " + language.name() + " in " + outputHumanLanguage))
- .setInputAttribute("type", "code")
- .setOutputAttrute("type", "description")
- .setOutputAttrute("style", settings.style)
- .setInputText(IndentedText.fromString(inputString).getTextBlock().trim())
- .buildCompletionRequest();
- }
-
- @Override
- public CharSequence postTransform(AnActionEvent event, CharSequence prompt, CharSequence completion) {
- CharSequence indent = UITools.getIndent(event);
- String wrapping = StringTools.lineWrapping(completion.toString().trim(), 120);
- return "\n" + indent + language.blockComment.fromString(wrapping).withIndent(indent) + "\n" + indent + prompt;
- }
- });
- }
-
- @NotNull
- protected AnAction genericInsert() {
- return new AnAction("_Insert Text", "Insert Text", null) {
- @Override
- public void actionPerformed(@NotNull AnActionEvent event) {
- Caret caret = event.getData(CommonDataKeys.CARET);
- Document document = caret.getEditor().getDocument();
- int caretPosition = caret.getOffset();
- CharSequence before = StringTools.getSuffixForContext(document.getText(new TextRange(0, caretPosition)));
- CharSequence after = StringTools.getPrefixForContext(document.getText(new TextRange(caretPosition, document.getTextLength())));
- AppSettingsState settings = AppSettingsState.getInstance();
- CompletionRequest completionRequest = settings.createCompletionRequest()
- .appendPrompt(before)
- .setSuffix(after);
- UITools.redoableRequest(completionRequest, "", event, complete -> {
- final Editor editor = event.getRequiredData(CommonDataKeys.EDITOR);
- return UITools.insertString(editor.getDocument(), caretPosition, complete);
- });
- }
- };
- }
-
- @NotNull
- protected AnAction genericAppend() {
- return new AnAction("_Append Text", "Append Text", null) {
- @Override
- public void actionPerformed(@NotNull AnActionEvent event) {
- Caret caret = event.getData(CommonDataKeys.CARET);
- CharSequence before = caret.getSelectedText();
- AppSettingsState settings = AppSettingsState.getInstance();
- CompletionRequest completionRequest = settings.createCompletionRequest()
- .appendPrompt(before);
- UITools.redoableRequest(completionRequest, "", event, complete -> {
- final Editor editor = event.getRequiredData(CommonDataKeys.EDITOR);
- return UITools.insertString(editor.getDocument(), caret.getSelectionEnd(), complete);
- });
- }
- };
- }
-
- @Nullable
- protected AnAction redoLast() {
- if(UITools.retry.isEmpty()) return null;
- return new AnAction("_Redo Last", "Redo last", null) {
- @Override
- public void actionPerformed(@NotNull AnActionEvent event) {
- UITools.retry.pop().run();
- }
- };
- }
-
- protected AnAction customEdit(String computerLanguage) {
- return TextReplacementAction.create("_Edit...", "Edit...", null, (event, string) -> {
- String instruction = JOptionPane.showInputDialog(null, "Instruction:", "Edit Code", JOptionPane.QUESTION_MESSAGE);
- AppSettingsState settings = AppSettingsState.getInstance();
- settings.addInstructionToHistory(instruction);
- return settings.createTranslationRequest()
- .setInputType(computerLanguage)
- .setOutputType(computerLanguage)
- .setInstruction(instruction)
- .setInputAttribute("type", "before")
- .setOutputAttrute("type", "after")
- .setInputText(IndentedText.fromString(string).getTextBlock())
- .buildCompletionRequest();
- });
- }
-
- /*
- *
- * Creates a new ActionGroup for recent edits.
- *
- * @param computerLanguage the language of the edit
- * @return a new ActionGroup for recent edits
- */
- @NotNull
- protected ActionGroup recentEdits(String computerLanguage) {
- return new ActionGroup("Recent Edits", true) {
- @Override
- public AnAction @NotNull [] getChildren(@Nullable AnActionEvent e) {
- ArrayList children = new ArrayList<>();
- AppSettingsState.getInstance().getEditHistory().forEach(instruction -> children.add(customEdit(computerLanguage, instruction)));
- return children.toArray(AnAction[]::new);
- }
- };
- }
-
- @NotNull
- protected AnAction docAction(String extension, ComputerLanguage language) {
- return new AnAction("_Add " + language.docStyle + " Comments", "Add " + language.docStyle + " Comments", null) {
- @Override
- public void actionPerformed(@NotNull final AnActionEvent event) {
- Caret caret = event.getData(CommonDataKeys.CARET);
- PsiFile psiFile = event.getRequiredData(CommonDataKeys.PSI_FILE);
- PsiElement smallestIntersectingMethod = PsiUtil.getSmallestIntersecting(psiFile, caret.getSelectionStart(), caret.getSelectionEnd());
- if (null == smallestIntersectingMethod) return;
- AppSettingsState settings = AppSettingsState.getInstance();
- String code = smallestIntersectingMethod.getText();
- IndentedText indentedInput = IndentedText.fromString(code);
- CharSequence indent = indentedInput.getIndent();
- CompletionRequest completionRequest = settings.createTranslationRequest()
- .setInputType(extension)
- .setOutputType(extension)
- .setInstruction(UITools.getInstruction("Rewrite to include detailed " + language.docStyle))
- .setInputAttribute("type", "uncommented")
- .setOutputAttrute("type", "commented")
- .setOutputAttrute("style", settings.style)
- .setInputText(indentedInput.getTextBlock())
- .buildCompletionRequest()
- .addStops(language.getMultilineCommentSuffix());
- int startOffset = smallestIntersectingMethod.getTextRange().getStartOffset();
- int endOffset = smallestIntersectingMethod.getTextRange().getEndOffset();
- UITools.redoableRequest(completionRequest, "", event, (CharSequence docString) -> {
- TextBlock reindented = language.docComment.fromString(docString.toString().trim()).withIndent(indent);
- final CharSequence newText = reindented + "\n" + indent + StringTools.trimPrefix(indentedInput.toString());
- final Editor editor = event.getRequiredData(CommonDataKeys.EDITOR);
- return UITools.replaceString(editor.getDocument(), startOffset, endOffset, newText);
- });
- }
- };
- }
-
- /**
- * Creates a paste action for the given language.
- *
- * @param language the language to paste into
- * @return a {@link TextReplacementAction} that pastes the contents of the clipboard into the given language
- */
- @NotNull
- protected AnAction pasteAction(@NotNull CharSequence language) {
- return TextReplacementAction.create("_Paste", "Paste", null, (event, string) -> {
- String text = CopyPasteManager.getInstance().getContents(DataFlavor.stringFlavor).toString().trim();
- return AppSettingsState.getInstance().createTranslationRequest()
- .setInputType("source")
- .setOutputType("translated")
- .setInstruction("Translate this input into " + language)
- .setInputAttribute("language", "autodetect")
- .setOutputAttrute("language", language)
- .setInputText(text)
- .buildCompletionRequest();
- });
- }
-
- @NotNull
- protected AnAction customEdit(CharSequence computerLanguage, CharSequence instruction) {
- return TextReplacementAction.create(instruction, instruction, null, (event, string) -> {
- AppSettingsState settings = AppSettingsState.getInstance();
- settings.addInstructionToHistory(instruction);
- return settings.createTranslationRequest()
- .setInputType(computerLanguage)
- .setOutputType(computerLanguage)
- .setInstruction(instruction)
- .setInputAttribute("type", "before")
- .setOutputAttrute("type", "after")
- .setInputText(IndentedText.fromString(string).getTextBlock())
- .buildCompletionRequest();
- });
- }
-
- @Nullable
- protected AnAction markdownListAction(@NotNull AnActionEvent e) {
- try {
- Caret caret = e.getData(CommonDataKeys.CARET);
- if (null == caret) return null;
- PsiFile psiFile = e.getData(CommonDataKeys.PSI_FILE);
- if (null == psiFile) return null;
- PsiElement list = PsiUtil.getSmallestIntersecting(psiFile, caret.getSelectionStart(), caret.getSelectionEnd(), "MarkdownListImpl");
- if (null == list) return null;
- return new AnAction("Add _List Items", "Add list items", null) {
- @Override
- public void actionPerformed(@NotNull AnActionEvent event) {
- AppSettingsState settings = AppSettingsState.getInstance();
- List items = StringTools.trim(PsiUtil.getAll(list, "MarkdownListItemImpl")
- .stream().map(item -> PsiUtil.getAll(item, "MarkdownParagraphImpl").get(0).getText()).collect(Collectors.toList()), 10, false);
- CharSequence indent = UITools.getIndent(caret);
- CharSequence n = Integer.toString(items.size() * 2);
- int endOffset = list.getTextRange().getEndOffset();
- String listPrefix = "* ";
- CompletionRequest completionRequest = settings.createTranslationRequest()
- .setInstruction(UITools.getInstruction("List " + n + " items"))
- .setInputType("instruction")
- .setInputText("List " + n + " items")
- .setOutputType("list")
- .setOutputAttrute("style", settings.style)
- .buildCompletionRequest()
- .appendPrompt(items.stream().map(x2 -> listPrefix + x2).collect(Collectors.joining("\n")) + "\n" + listPrefix);
- UITools.redoableRequest(completionRequest, "", event, complete -> {
- List newItems = Arrays.stream(complete.toString().split("\n")).map(String::trim)
- .filter(x1 -> x1 != null && x1.length() > 0).map(x1 -> StringTools.stripPrefix(x1, listPrefix)).collect(Collectors.toList());
- String strippedList = Arrays.stream(list.getText().split("\n"))
- .map(String::trim).filter(x -> x.length() > 0).collect(Collectors.joining("\n"));
- String bulletString = Stream.of("- [ ] ", "- ", "* ")
- .filter(strippedList::startsWith).findFirst().orElse("1. ");
- CharSequence itemText = indent + newItems.stream().map(x -> bulletString + x)
- .collect(Collectors.joining("\n" + indent));
- final Editor editor = event.getRequiredData(CommonDataKeys.EDITOR);
- return UITools.insertString(editor.getDocument(), endOffset, "\n" + itemText);
- });
- }
- };
- } catch (Exception ex) {
- log.error(ex);
- return null;
- }
- }
-
- /**
- * This method creates an action to add new columns to a Markdown table.
- *
- * @param e The action event
- * @return An action to add new columns to a Markdown table, or null if the action cannot be created
- */
- @Nullable
- protected AnAction markdownNewTableColsAction(@NotNull AnActionEvent e) {
- Caret caret = e.getData(CommonDataKeys.CARET);
- if (null == caret) return null;
- PsiFile psiFile = e.getData(CommonDataKeys.PSI_FILE);
- if (null == psiFile) return null;
- PsiElement table = PsiUtil.getSmallestIntersecting(psiFile, caret.getSelectionStart(), caret.getSelectionEnd(), "MarkdownTableImpl");
- if (null == table) return null;
- List rows = Arrays.asList(StringTools.transposeMarkdownTable(PsiUtil.getAll(table, "MarkdownTableRowImpl")
- .stream().map(PsiElement::getText).collect(Collectors.joining("\n")), false, false).split("\n"));
- CharSequence n = Integer.toString(rows.size() * 2);
- return new AnAction("Add _Table Columns", "Add table columns", null) {
- @Override
- public void actionPerformed(@NotNull AnActionEvent event) {
- CharSequence originalText = table.getText();
- AppSettingsState settings = AppSettingsState.getInstance();
- CharSequence indent = UITools.getIndent(caret);
- UITools.redoableRequest(newRowsRequest(settings, n, rows, ""),
- "",
- event,
- (CharSequence complete) -> {
- List newRows = Arrays.stream(("" + complete).split("\n")).map(String::trim)
- .filter(x -> x.length() > 0).collect(Collectors.toList());
- String newTableTxt = StringTools.transposeMarkdownTable(Stream.concat(rows.stream(), newRows.stream())
- .collect(Collectors.joining("\n")), false, true);
- final Editor editor = event.getRequiredData(CommonDataKeys.EDITOR);
- return UITools.replaceString(
- editor.getDocument(),
- table.getTextRange().getStartOffset(),
- table.getTextRange().getEndOffset(),
- newTableTxt.replace("\n", "\n" + indent));
- });
- }
- };
- }
-
- /**
- * Creates an action to add a new column to a Markdown table.
- *
- * @param e The action event.
- * @return An action to add a new column to a Markdown table, or null if the action cannot be created.
- */
- @Nullable
- protected AnAction markdownNewTableColAction(@NotNull AnActionEvent e) {
- Caret caret = e.getData(CommonDataKeys.CARET);
- if (null == caret) return null;
- PsiFile psiFile = e.getData(CommonDataKeys.PSI_FILE);
- if (null == psiFile) return null;
- PsiElement table = PsiUtil.getSmallestIntersecting(psiFile, caret.getSelectionStart(), caret.getSelectionEnd(), "MarkdownTableImpl");
- if (null == table) return null;
- List rows = Arrays.asList(StringTools.transposeMarkdownTable(PsiUtil.getAll(table, "MarkdownTableRowImpl")
- .stream().map(PsiElement::getText).collect(Collectors.joining("\n")), false, false).split("\n"));
- CharSequence n = Integer.toString(rows.size() * 2);
- return new AnAction("Add Table _Column...", "Add table column...", null) {
- @Override
- public void actionPerformed(@NotNull AnActionEvent event) {
- AppSettingsState settings = AppSettingsState.getInstance();
- CharSequence indent = UITools.getIndent(caret);
- CharSequence columnName = JOptionPane.showInputDialog(null, "Column Name:", "Add Column", JOptionPane.QUESTION_MESSAGE);
- UITools.redoableRequest(
- newRowsRequest(settings, n, rows, "| " + columnName + " | "),
- "",
- event,
- (CharSequence complete) -> {
- List newRows = Arrays.stream(("" + complete).split("\n"))
- .map(String::trim).filter(x -> x.length() > 0).collect(Collectors.toList());
- String newTableTxt = StringTools.transposeMarkdownTable(Stream.concat(rows.stream(),
- newRows.stream()).collect(Collectors.joining("\n")), false, true);
- final Editor editor = event.getRequiredData(CommonDataKeys.EDITOR);
- return UITools.replaceString(
- editor.getDocument(),
- table.getTextRange().getStartOffset(),
- table.getTextRange().getEndOffset(),
- newTableTxt.replace("\n", "\n" + indent));
- });
- }
- };
- }
-
- @Nullable
- protected AnAction markdownNewTableRowsAction(@NotNull AnActionEvent e) {
- Caret caret = e.getData(CommonDataKeys.CARET);
- if (null == caret) return null;
- PsiFile psiFile = e.getData(CommonDataKeys.PSI_FILE);
- if (null == psiFile) return null;
- PsiElement table = PsiUtil.getSmallestIntersecting(psiFile, caret.getSelectionStart(), caret.getSelectionEnd(), "MarkdownTableImpl");
- if (null == table) return null;
- if (null != table) {
- List rows = StringTools.trim(PsiUtil.getAll(table, "MarkdownTableRowImpl")
- .stream().map(PsiElement::getText).collect(Collectors.toList()), 10, true);
- CharSequence n = Integer.toString(rows.size() * 2);
- return new AnAction("Add _Table Rows", "Add table rows", null) {
- @Override
- public void actionPerformed(@NotNull AnActionEvent event) {
- AppSettingsState settings = AppSettingsState.getInstance();
- CharSequence indent = UITools.getIndent(caret);
- UITools.redoableRequest(newRowsRequest(settings, n, rows, ""),
- "",
- event,
- (CharSequence complete) -> {
- List newRows = Arrays.stream(("" + complete).split("\n"))
- .map(String::trim).filter(x -> x.length() > 0).collect(Collectors.toList());
- CharSequence itemText = indent + newRows.stream().collect(Collectors.joining("\n" + indent));
- final Editor editor = event.getRequiredData(CommonDataKeys.EDITOR);
- return UITools.insertString(editor.getDocument(), table.getTextRange().getEndOffset(), "\n" + itemText);
- });
- }
- };
- }
- return null;
- }
-
- @NotNull
- protected CompletionRequest newRowsRequest(AppSettingsState settings, CharSequence n, List rows, CharSequence rowPrefix) {
- return settings.createTranslationRequest()
- .setInstruction(UITools.getInstruction("List " + n + " items"))
- .setInputType("instruction")
- .setInputText("List " + n + " items")
- .setOutputType("markdown")
- .setOutputAttrute("style", settings.style)
- .buildCompletionRequest()
- .appendPrompt("\n" + String.join("\n", rows) + "\n" + rowPrefix);
- }
-
- /**
- * Creates a {@link TextReplacementAction} for the given {@link AnActionEvent} and human language.
- *
- * @param e the action event
- * @param humanLanguage the human language
- * @return the {@link TextReplacementAction} or {@code null} if no action can be created
- */
- @Nullable
- protected AnAction markdownContextAction(@NotNull AnActionEvent e, CharSequence humanLanguage) {
- Caret caret = e.getData(CommonDataKeys.CARET);
- if (null != caret) {
- int selectionStart = caret.getSelectionStart();
- int selectionEnd = caret.getSelectionEnd();
- if (selectionStart < selectionEnd) {
- return TextReplacementAction.create("E_xecute Directive", "Execute Directive", null, (event, humanDescription) -> {
- AppSettingsState settings = AppSettingsState.getInstance();
- PsiFile psiFile = e.getRequiredData(CommonDataKeys.PSI_FILE);
- String context = PsiMarkdownContext.getContext(psiFile, selectionStart, selectionEnd).toString(selectionEnd);
- context = context + "\n\n";
- context = context + "\n";
- return settings.createTranslationRequest()
- .setOutputType("markdown")
- .setInstruction(UITools.getInstruction(String.format("Using Markdown and %s", humanLanguage)))
- .setInputType("instruction")
- .setInputText(humanDescription)
- .setOutputAttrute("type", "document")
- .setOutputAttrute("style", settings.style)
- .buildCompletionRequest()
- .appendPrompt(context);
- });
- }
- }
- return null;
- }
-
- public static void addIfNotNull(@NotNull ArrayList children, AnAction action) {
- if (null != action) children.add(action);
- }
-
- @Nullable
- protected AnAction printTreeAction(@NotNull AnActionEvent e) {
- Caret caret = e.getData(CommonDataKeys.CARET);
- if (null == caret) return null;
- PsiElement psiFile = e.getData(CommonDataKeys.PSI_FILE);
- if (null == psiFile) return null;
- int selectionStart = caret.getSelectionStart();
- int selectionEnd = caret.getSelectionEnd();
- PsiElement largestContainedEntity = PsiUtil.getLargestContainedEntity(psiFile, selectionStart, selectionEnd);
- if (largestContainedEntity != null) psiFile = largestContainedEntity;
- PsiElement finalPsiFile = psiFile;
- return new AnAction("Print PSI Tree", "Print PSI Tree", null) {
- @Override
- public void actionPerformed(@NotNull final AnActionEvent e1) {
- log.warn(PsiUtil.printTree(finalPsiFile));
- }
-
- };
- }
-
- @Nullable
- protected AnAction rewordCommentAction(@NotNull AnActionEvent e, ComputerLanguage computerLanguage, String humanLanguage) {
- PsiFile psiFile = e.getData(CommonDataKeys.PSI_FILE);
- if (null == psiFile) return null;
- Caret caret = e.getData(CommonDataKeys.CARET);
- if (null == caret) return null;
- int selectionStart = caret.getSelectionStart();
- int selectionEnd = caret.getSelectionEnd();
- PsiElement largestIntersectingComment = PsiUtil.getLargestIntersectingComment(psiFile, selectionStart, selectionEnd);
- if (largestIntersectingComment == null) return null;
- return new AnAction("_Reword Comment", "Reword Comment", null) {
- @Override
- public void actionPerformed(@NotNull final AnActionEvent e1) {
- final Editor editor = e1.getRequiredData(CommonDataKeys.EDITOR);
- AppSettingsState settings = AppSettingsState.getInstance();
- String text = largestIntersectingComment.getText();
- TextBlockFactory> commentModel = computerLanguage.getCommentModel(text);
- String commentText = commentModel.fromString(text.trim()).stream()
- .map(Object::toString)
- .map(String::trim)
- .filter(x -> !x.isEmpty())
- .reduce((a, b) -> a + "\n" + b).get();
- int startOffset = largestIntersectingComment.getTextRange().getStartOffset();
- int endOffset = largestIntersectingComment.getTextRange().getEndOffset();
- CharSequence indent = UITools.getIndent(caret);
- UITools.redoableRequest(settings.createTranslationRequest()
- .setInstruction(UITools.getInstruction("Reword"))
- .setInputText(commentText)
- .setInputType(humanLanguage)
- .setOutputAttrute("type", "input")
- .setOutputType(humanLanguage)
- .setOutputAttrute("type", "output")
- .setOutputAttrute("style", settings.style)
- .buildCompletionRequest(), "", e1, (CharSequence result) -> {
- String lineWrapping = StringTools.lineWrapping(result, 120);
- CharSequence finalResult = indent.toString() + commentModel.fromString(lineWrapping).withIndent(indent);
- return UITools.replaceString(editor.getDocument(), startOffset, endOffset, finalResult);
- });
- }
-
- };
- }
-
- @Nullable
- protected AnAction psiClassContextAction(@NotNull AnActionEvent e, ComputerLanguage computerLanguage, String humanLanguage) {
- PsiFile psiFile = e.getData(CommonDataKeys.PSI_FILE);
- if (null == psiFile) return null;
- Caret caret = e.getData(CommonDataKeys.CARET);
- if (null == caret) return null;
- int selectionStart = caret.getSelectionStart();
- int selectionEnd = caret.getSelectionEnd();
- PsiElement largestIntersectingComment = PsiUtil.getLargestIntersectingComment(psiFile, selectionStart, selectionEnd);
- if (largestIntersectingComment == null) return null;
- return new AnAction("Insert _Implementation", "Insert Implementation", null) {
- @Override
- public void actionPerformed(@NotNull final AnActionEvent e1) {
- final Editor editor = e1.getRequiredData(CommonDataKeys.EDITOR);
- final CaretModel caretModel = editor.getCaretModel();
- final Caret primaryCaret = caretModel.getPrimaryCaret();
- @NotNull String selectedText = primaryCaret.getSelectedText();
- AppSettingsState settings = AppSettingsState.getInstance();
-
- String instruct = (selectedText.split(" ").length > 4 ? selectedText : largestIntersectingComment.getText()).trim();
- String specification = computerLanguage.getCommentModel(instruct).fromString(instruct).stream()
- .map(Object::toString)
- .map(String::trim)
- .filter(x -> !x.isEmpty())
- .reduce((a, b) -> a + " " + b).get();
- int endOffset = largestIntersectingComment.getTextRange().getEndOffset();
- UITools.redoableRequest(settings.createTranslationRequest()
- .setInstruction("Implement " + humanLanguage + " as " + computerLanguage.name() + " code")
- .setInputType(humanLanguage)
- .setInputAttribute("type", "instruction")
- .setInputText(specification)
- .setOutputType(computerLanguage.name())
- .setOutputAttrute("type", "code")
- .setOutputAttrute("style", settings.style)
- .buildCompletionRequest()
- .appendPrompt(PsiClassContext.getContext(psiFile, selectionStart, selectionEnd) + "\n"),
- UITools.getIndent(caret),
- e1,
- (CharSequence result) -> UITools.insertString(editor.getDocument(), endOffset, "\n" + result));
- }
- };
- }
-
-
- @Override
- public void update(@NotNull AnActionEvent e) {
- e.getPresentation().setEnabledAndVisible(true);
- super.update(e);
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/github/simiacryptus/aicoder/actions/CommentsAction.java b/src/main/java/com/github/simiacryptus/aicoder/actions/CommentsAction.java
new file mode 100644
index 00000000..35113ec6
--- /dev/null
+++ b/src/main/java/com/github/simiacryptus/aicoder/actions/CommentsAction.java
@@ -0,0 +1,61 @@
+package com.github.simiacryptus.aicoder.actions;
+
+import com.github.simiacryptus.aicoder.config.AppSettingsState;
+import com.github.simiacryptus.aicoder.openai.CompletionRequest;
+import com.github.simiacryptus.aicoder.util.ComputerLanguage;
+import com.github.simiacryptus.aicoder.util.UITools;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.editor.Caret;
+import com.intellij.openapi.editor.CaretModel;
+import com.intellij.openapi.editor.Editor;
+import org.jetbrains.annotations.NotNull;
+
+import static com.github.simiacryptus.aicoder.util.UITools.replaceString;
+import static java.util.Objects.requireNonNull;
+
+public class CommentsAction extends AnAction {
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ e.getPresentation().setEnabledAndVisible(isEnabled(e));
+ super.update(e);
+ }
+
+ private static boolean isEnabled(@NotNull AnActionEvent e) {
+ final Editor editor = e.getRequiredData(CommonDataKeys.EDITOR);
+ final CaretModel caretModel = editor.getCaretModel();
+ final Caret primaryCaret = caretModel.getPrimaryCaret();
+ int selectionStart = primaryCaret.getSelectionStart();
+ int selectionEnd = primaryCaret.getSelectionEnd();
+ if(selectionStart == selectionEnd) return false;
+ if (null == ComputerLanguage.getComputerLanguage(e)) return false;
+ return true;
+ }
+
+ @Override
+ public void actionPerformed(@NotNull final AnActionEvent e) {
+ final Editor editor = e.getRequiredData(CommonDataKeys.EDITOR);
+ final CaretModel caretModel = editor.getCaretModel();
+ final Caret primaryCaret = caretModel.getPrimaryCaret();
+ int selectionStart = primaryCaret.getSelectionStart();
+ int selectionEnd = primaryCaret.getSelectionEnd();
+ String selectedText = primaryCaret.getSelectedText();
+ String outputHumanLanguage = AppSettingsState.getInstance().humanLanguage;
+ ComputerLanguage language = ComputerLanguage.getComputerLanguage(e);
+ AppSettingsState settings = AppSettingsState.getInstance();
+ CompletionRequest request = settings.createTranslationRequest()
+ .setInputType(requireNonNull(language).name())
+ .setOutputType(language.name())
+ .setInstruction(UITools.getInstruction("Rewrite to include detailed " + outputHumanLanguage + " code comments for every line"))
+ .setInputAttribute("type", "uncommented")
+ .setOutputAttrute("type", "commented")
+ .setOutputAttrute("style", settings.style)
+ .setInputText(selectedText)
+ .buildCompletionRequest();
+ Caret caret = e.getData(CommonDataKeys.CARET);
+ CharSequence indent = UITools.getIndent(caret);
+ UITools.redoableRequest(request, indent, e, newText -> replaceString(editor.getDocument(), selectionStart, selectionEnd, newText));
+ }
+}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/actions/CustomEditAction.java b/src/main/java/com/github/simiacryptus/aicoder/actions/CustomEditAction.java
new file mode 100644
index 00000000..40be79e0
--- /dev/null
+++ b/src/main/java/com/github/simiacryptus/aicoder/actions/CustomEditAction.java
@@ -0,0 +1,57 @@
+package com.github.simiacryptus.aicoder.actions;
+
+import com.github.simiacryptus.aicoder.util.ComputerLanguage;
+import com.github.simiacryptus.aicoder.config.AppSettingsState;
+import com.github.simiacryptus.aicoder.openai.CompletionRequest;
+import com.github.simiacryptus.aicoder.util.IndentedText;
+import com.github.simiacryptus.aicoder.util.UITools;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.editor.Caret;
+import com.intellij.openapi.editor.CaretModel;
+import com.intellij.openapi.editor.Editor;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+
+import static com.github.simiacryptus.aicoder.util.UITools.replaceString;
+import static java.util.Objects.requireNonNull;
+
+public class CustomEditAction extends AnAction {
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ e.getPresentation().setEnabledAndVisible(isEnabled(e));
+ super.update(e);
+ }
+
+ private static boolean isEnabled(@NotNull AnActionEvent e) {
+ return null != ComputerLanguage.getComputerLanguage(e);
+ }
+
+ @Override
+ public void actionPerformed(@NotNull final AnActionEvent e) {
+ final Editor editor = e.getRequiredData(CommonDataKeys.EDITOR);
+ final CaretModel caretModel = editor.getCaretModel();
+ final Caret primaryCaret = caretModel.getPrimaryCaret();
+ int selectionStart = primaryCaret.getSelectionStart();
+ int selectionEnd = primaryCaret.getSelectionEnd();
+ String selectedText = primaryCaret.getSelectedText();
+ String computerLanguage = requireNonNull(ComputerLanguage.getComputerLanguage(e)).name();
+ String instruction = JOptionPane.showInputDialog(null, "Instruction:", "Edit Code", JOptionPane.QUESTION_MESSAGE);
+ AppSettingsState settings = AppSettingsState.getInstance();
+ settings.addInstructionToHistory(instruction);
+ CompletionRequest request = settings.createTranslationRequest()
+ .setInputType(computerLanguage)
+ .setOutputType(computerLanguage)
+ .setInstruction(instruction)
+ .setInputAttribute("type", "before")
+ .setOutputAttrute("type", "after")
+ .setInputText(IndentedText.fromString(selectedText).getTextBlock())
+ .buildCompletionRequest();
+ Caret caret = e.getData(CommonDataKeys.CARET);
+ CharSequence indent = UITools.getIndent(caret);
+ UITools.redoableRequest(request, indent, e, newText -> replaceString(editor.getDocument(), selectionStart, selectionEnd, newText));
+ }
+}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/actions/DescribeAction.java b/src/main/java/com/github/simiacryptus/aicoder/actions/DescribeAction.java
new file mode 100644
index 00000000..c5a86822
--- /dev/null
+++ b/src/main/java/com/github/simiacryptus/aicoder/actions/DescribeAction.java
@@ -0,0 +1,82 @@
+package com.github.simiacryptus.aicoder.actions;
+
+import com.github.simiacryptus.aicoder.util.*;
+import com.github.simiacryptus.aicoder.config.AppSettingsState;
+import com.github.simiacryptus.aicoder.openai.CompletionRequest;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.editor.Caret;
+import com.intellij.openapi.editor.CaretModel;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+
+import static com.github.simiacryptus.aicoder.util.UITools.replaceString;
+
+public class DescribeAction extends AnAction {
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ e.getPresentation().setEnabledAndVisible(isEnabled(e));
+ super.update(e);
+ }
+
+ private static boolean isEnabled(@NotNull AnActionEvent e) {
+ return null != ComputerLanguage.getComputerLanguage(e);
+ }
+
+ @Override
+ public void actionPerformed(@NotNull final AnActionEvent event) {
+ final Editor editor = event.getRequiredData(CommonDataKeys.EDITOR);
+ final CaretModel caretModel = editor.getCaretModel();
+ final Caret primaryCaret = caretModel.getPrimaryCaret();
+ int selectionStart = primaryCaret.getSelectionStart();
+ int selectionEnd = primaryCaret.getSelectionEnd();
+ String selectedText = primaryCaret.getSelectedText();
+ ComputerLanguage language = ComputerLanguage.getComputerLanguage(event);
+ assert language != null;
+
+ if(null == selectedText || selectedText.isEmpty()) {
+ Document document = editor.getDocument();
+ int lineNumber = document.getLineNumber(selectionStart);
+ int lineStartOffset = document.getLineStartOffset(lineNumber);
+ int lineEndOffset = document.getLineEndOffset(lineNumber);
+ String currentLine = document.getText().substring(lineStartOffset, lineEndOffset);
+ selectionStart = lineStartOffset;
+ selectionEnd = lineEndOffset;
+ selectedText = currentLine;
+ }
+
+ actionPerformed(event, editor, selectionStart, selectionEnd, selectedText, language);
+ }
+
+ private static void actionPerformed(@NotNull AnActionEvent event, Editor editor, int selectionStart, int selectionEnd, String selectedText, ComputerLanguage language) {
+ CharSequence indent = UITools.getIndent(event);
+ AppSettingsState settings = AppSettingsState.getInstance();
+ CompletionRequest request = settings.createTranslationRequest()
+ .setInputType(Objects.requireNonNull(language).name())
+ .setOutputType(settings.humanLanguage)
+ .setInstruction(UITools.getInstruction("Explain this " + language.name() + " in " + settings.humanLanguage))
+ .setInputAttribute("type", "code")
+ .setOutputAttrute("type", "description")
+ .setOutputAttrute("style", settings.style)
+ .setInputText(IndentedText.fromString(selectedText).getTextBlock().trim())
+ .buildCompletionRequest();
+ UITools.redoableRequest(request, indent, event, newText -> transformCompletion(selectedText, language, indent, newText), newText -> replaceString(editor.getDocument(), selectionStart, selectionEnd, newText));
+ }
+
+ @NotNull
+ private static CharSequence transformCompletion(String selectedText, ComputerLanguage language, CharSequence indent, CharSequence x) {
+ String wrapping = StringTools.lineWrapping(x.toString().trim(), 120);
+ TextBlockFactory> commentStyle;
+ if(wrapping.trim().split("\n").length == 1) {
+ commentStyle = language.lineComment;
+ } else {
+ commentStyle = language.blockComment;
+ }
+ return "\n" + indent + Objects.requireNonNull(commentStyle).fromString(wrapping).withIndent(indent) + "\n" + indent + selectedText;
+ }
+}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/actions/DocAction.java b/src/main/java/com/github/simiacryptus/aicoder/actions/DocAction.java
new file mode 100644
index 00000000..6dab4f01
--- /dev/null
+++ b/src/main/java/com/github/simiacryptus/aicoder/actions/DocAction.java
@@ -0,0 +1,67 @@
+package com.github.simiacryptus.aicoder.actions;
+
+import com.github.simiacryptus.aicoder.config.AppSettingsState;
+import com.github.simiacryptus.aicoder.openai.CompletionRequest;
+import com.github.simiacryptus.aicoder.util.psi.PsiUtil;
+import com.github.simiacryptus.aicoder.util.*;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.editor.Caret;
+import com.intellij.openapi.editor.Document;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+
+import static com.github.simiacryptus.aicoder.util.UITools.replaceString;
+
+public class DocAction extends AnAction {
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ e.getPresentation().setEnabledAndVisible(isEnabled(e));
+ super.update(e);
+ }
+
+ private static boolean isEnabled(@NotNull AnActionEvent e) {
+ ComputerLanguage computerLanguage = ComputerLanguage.getComputerLanguage(e);
+ if (null == computerLanguage) return false;
+ if (null == computerLanguage.docStyle || computerLanguage.docStyle.isEmpty()) return false;
+ return true;
+ }
+
+ @Override
+ public void actionPerformed(@NotNull final AnActionEvent event) {
+ ComputerLanguage language = ComputerLanguage.getComputerLanguage(event);
+ Caret caret = event.getData(CommonDataKeys.CARET);
+ PsiFile psiFile = event.getRequiredData(CommonDataKeys.PSI_FILE);
+ PsiElement smallestIntersectingMethod = PsiUtil.getSmallestIntersecting(psiFile, Objects.requireNonNull(caret).getSelectionStart(), caret.getSelectionEnd());
+ if (null == smallestIntersectingMethod) return;
+ AppSettingsState settings = AppSettingsState.getInstance();
+ String code = smallestIntersectingMethod.getText();
+ IndentedText indentedInput = IndentedText.fromString(code);
+ CharSequence indent = indentedInput.getIndent();
+ int startOffset = smallestIntersectingMethod.getTextRange().getStartOffset();
+ int endOffset = smallestIntersectingMethod.getTextRange().getEndOffset();
+ CompletionRequest completionRequest = settings.createTranslationRequest()
+ .setInputType(Objects.requireNonNull(language).name())
+ .setOutputType(language.name())
+ .setInstruction(UITools.getInstruction("Rewrite to include detailed " + language.docStyle))
+ .setInputAttribute("type", "uncommented")
+ .setOutputAttrute("type", "commented")
+ .setOutputAttrute("style", settings.style)
+ .setInputText(indentedInput.getTextBlock())
+ .buildCompletionRequest()
+ .addStops(Objects.requireNonNull(language.getMultilineCommentSuffix()));
+ Document document = event.getRequiredData(CommonDataKeys.EDITOR).getDocument();
+ UITools.redoableRequest(completionRequest, "", event, docString -> transformCompletion(language, indentedInput, indent, docString), docString -> replaceString(document, startOffset, endOffset, docString));
+ }
+
+ @NotNull
+ private static CharSequence transformCompletion(ComputerLanguage language, IndentedText indentedInput, CharSequence indent, CharSequence docString) {
+ TextBlock reindented = Objects.requireNonNull(language.docComment).fromString(docString.toString().trim()).withIndent(indent);
+ return reindented + "\n" + indent + StringTools.trimPrefix(indentedInput.toString());
+ }
+}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/actions/FromHumanLanguageAction.java b/src/main/java/com/github/simiacryptus/aicoder/actions/FromHumanLanguageAction.java
new file mode 100644
index 00000000..4c2c9c6f
--- /dev/null
+++ b/src/main/java/com/github/simiacryptus/aicoder/actions/FromHumanLanguageAction.java
@@ -0,0 +1,44 @@
+package com.github.simiacryptus.aicoder.actions;
+
+import com.github.simiacryptus.aicoder.util.ComputerLanguage;
+import com.github.simiacryptus.aicoder.config.AppSettingsState;
+import com.github.simiacryptus.aicoder.openai.CompletionRequest;
+import com.github.simiacryptus.aicoder.util.UITools;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.editor.Caret;
+import com.intellij.openapi.editor.CaretModel;
+import com.intellij.openapi.editor.Editor;
+import org.jetbrains.annotations.NotNull;
+
+import static com.github.simiacryptus.aicoder.util.UITools.replaceString;
+import static java.util.Objects.requireNonNull;
+
+public class FromHumanLanguageAction extends AnAction {
+
+ public FromHumanLanguageAction() {
+ super("", "", null);
+ }
+
+ @Override
+ public void actionPerformed(@NotNull final AnActionEvent event) {
+ final Editor editor = event.getRequiredData(CommonDataKeys.EDITOR);
+ final CaretModel caretModel = editor.getCaretModel();
+ final Caret primaryCaret = caretModel.getPrimaryCaret();
+ int selectionStart = primaryCaret.getSelectionStart();
+ int selectionEnd = primaryCaret.getSelectionEnd();
+ String selectedText = primaryCaret.getSelectedText();
+ CompletionRequest request = AppSettingsState.getInstance().createTranslationRequest()
+ .setInputType(AppSettingsState.getInstance().humanLanguage.toLowerCase())
+ .setOutputType(requireNonNull(ComputerLanguage.getComputerLanguage(event)).name())
+ .setInstruction("Implement this specification")
+ .setInputAttribute("type", "input")
+ .setOutputAttrute("type", "output")
+ .setInputText(selectedText)
+ .buildCompletionRequest();
+ Caret caret = event.getData(CommonDataKeys.CARET);
+ CharSequence indent = UITools.getIndent(caret);
+ UITools.redoableRequest(request, indent, event, newText -> replaceString(editor.getDocument(), selectionStart, selectionEnd, newText));
+ }
+}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/actions/GenericAppend.java b/src/main/java/com/github/simiacryptus/aicoder/actions/GenericAppend.java
new file mode 100644
index 00000000..06529d4d
--- /dev/null
+++ b/src/main/java/com/github/simiacryptus/aicoder/actions/GenericAppend.java
@@ -0,0 +1,40 @@
+package com.github.simiacryptus.aicoder.actions;
+
+import com.github.simiacryptus.aicoder.config.AppSettingsState;
+import com.github.simiacryptus.aicoder.openai.CompletionRequest;
+import com.github.simiacryptus.aicoder.util.UITools;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.editor.Caret;
+import com.intellij.openapi.editor.Document;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+
+public class GenericAppend extends AnAction {
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ e.getPresentation().setEnabledAndVisible(isEnabled(e));
+ super.update(e);
+ }
+
+ @SuppressWarnings("unused")
+ private static boolean isEnabled(@NotNull AnActionEvent e) {
+ Caret data = e.getData(CommonDataKeys.CARET);
+ if (!data.hasSelection()) return false;
+ return true;
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent event) {
+ Caret caret = event.getData(CommonDataKeys.CARET);
+ CharSequence before = Objects.requireNonNull(caret).getSelectedText();
+ AppSettingsState settings = AppSettingsState.getInstance();
+ CompletionRequest completionRequest = settings.createCompletionRequest().appendPrompt(before);
+ Document document = event.getRequiredData(CommonDataKeys.EDITOR).getDocument();
+ int selectionEnd = caret.getSelectionEnd();
+ UITools.redoableRequest(completionRequest, "", event, newText -> UITools.insertString(document, selectionEnd, newText));
+ }
+}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/actions/GenericInsert.java b/src/main/java/com/github/simiacryptus/aicoder/actions/GenericInsert.java
new file mode 100644
index 00000000..53f63d2a
--- /dev/null
+++ b/src/main/java/com/github/simiacryptus/aicoder/actions/GenericInsert.java
@@ -0,0 +1,45 @@
+package com.github.simiacryptus.aicoder.actions;
+
+import com.github.simiacryptus.aicoder.config.AppSettingsState;
+import com.github.simiacryptus.aicoder.openai.CompletionRequest;
+import com.github.simiacryptus.aicoder.util.StringTools;
+import com.github.simiacryptus.aicoder.util.UITools;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.editor.Caret;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.util.TextRange;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+
+public class GenericInsert extends AnAction {
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ e.getPresentation().setEnabledAndVisible(isEnabled(e));
+ super.update(e);
+ }
+
+ @SuppressWarnings("unused")
+ private static boolean isEnabled(@NotNull AnActionEvent e) {
+ Caret data = e.getData(CommonDataKeys.CARET);
+ if(data.hasSelection()) return false;
+ return true;
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent event) {
+ Caret caret = event.getData(CommonDataKeys.CARET);
+ Document document = Objects.requireNonNull(caret).getEditor().getDocument();
+ int caretPosition = caret.getOffset();
+ CharSequence before = StringTools.getSuffixForContext(document.getText(new TextRange(0, caretPosition)));
+ CharSequence after = StringTools.getPrefixForContext(document.getText(new TextRange(caretPosition, document.getTextLength())));
+ AppSettingsState settings = AppSettingsState.getInstance();
+ CompletionRequest completionRequest = settings.createCompletionRequest()
+ .appendPrompt(before)
+ .setSuffix(after);
+ UITools.redoableRequest(completionRequest, "", event, newText -> UITools.insertString(document, caretPosition, newText));
+ }
+}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/actions/MarkdownContextAction.java b/src/main/java/com/github/simiacryptus/aicoder/actions/MarkdownContextAction.java
new file mode 100644
index 00000000..6211c886
--- /dev/null
+++ b/src/main/java/com/github/simiacryptus/aicoder/actions/MarkdownContextAction.java
@@ -0,0 +1,86 @@
+package com.github.simiacryptus.aicoder.actions;
+
+import com.github.simiacryptus.aicoder.config.AppSettingsState;
+import com.github.simiacryptus.aicoder.openai.CompletionRequest;
+import com.github.simiacryptus.aicoder.util.psi.PsiMarkdownContext;
+import com.github.simiacryptus.aicoder.util.UITools;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.editor.Caret;
+import com.intellij.openapi.editor.CaretModel;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.psi.PsiFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import static com.github.simiacryptus.aicoder.util.UITools.replaceString;
+import static java.util.Objects.requireNonNull;
+
+public class MarkdownContextAction extends AnAction {
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ e.getPresentation().setEnabledAndVisible(isEnabled(e));
+ super.update(e);
+ }
+
+ private static boolean isEnabled(@NotNull AnActionEvent e) {
+ return null != getMarkdownContextParams(e, AppSettingsState.getInstance().humanLanguage);
+ }
+
+ @Nullable
+ public static MarkdownContextParams getMarkdownContextParams(@NotNull AnActionEvent e, CharSequence humanLanguage) {
+ Caret caret = e.getData(CommonDataKeys.CARET);
+ if (null != caret) {
+ int selectionStart = caret.getSelectionStart();
+ int selectionEnd = caret.getSelectionEnd();
+ if (selectionStart < selectionEnd) {
+ return new MarkdownContextParams(humanLanguage, selectionStart, selectionEnd);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void actionPerformed(@NotNull final AnActionEvent event) {
+ final Editor editor = event.getRequiredData(CommonDataKeys.EDITOR);
+ final CaretModel caretModel = editor.getCaretModel();
+ final Caret primaryCaret = caretModel.getPrimaryCaret();
+ int selectionStart = primaryCaret.getSelectionStart();
+ int selectionEnd = primaryCaret.getSelectionEnd();
+ String selectedText = primaryCaret.getSelectedText();
+ String humanLanguage = AppSettingsState.getInstance().humanLanguage;
+ MarkdownContextParams markdownContextParams = getMarkdownContextParams(event, humanLanguage);
+ AppSettingsState settings = AppSettingsState.getInstance();
+ PsiFile psiFile = event.getRequiredData(CommonDataKeys.PSI_FILE);
+ String context = PsiMarkdownContext.getContext(psiFile, requireNonNull(markdownContextParams).selectionStart, markdownContextParams.selectionEnd).toString(markdownContextParams.selectionEnd);
+ context = context + "\n\n";
+ context = context + "\n";
+ CompletionRequest request = settings.createTranslationRequest()
+ .setOutputType("markdown")
+ .setInstruction(UITools.getInstruction(String.format("Using Markdown and %s", markdownContextParams.humanLanguage)))
+ .setInputType("instruction")
+ .setInputText(selectedText)
+ .setOutputAttrute("type", "document")
+ .setOutputAttrute("style", settings.style)
+ .buildCompletionRequest()
+ .appendPrompt(context);
+ Caret caret = event.getData(CommonDataKeys.CARET);
+ CharSequence indent = UITools.getIndent(caret);
+ UITools.redoableRequest(request, indent, event, newText -> replaceString(editor.getDocument(), selectionStart, selectionEnd, newText));
+ }
+
+ public static class MarkdownContextParams {
+ public final CharSequence humanLanguage;
+ public final int selectionStart;
+ public final int selectionEnd;
+
+ private MarkdownContextParams(CharSequence humanLanguage, int selectionStart, int selectionEnd) {
+ this.humanLanguage = humanLanguage;
+ this.selectionStart = selectionStart;
+ this.selectionEnd = selectionEnd;
+ }
+
+ }
+}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/actions/MarkdownListAction.java b/src/main/java/com/github/simiacryptus/aicoder/actions/MarkdownListAction.java
new file mode 100644
index 00000000..f6e1eccb
--- /dev/null
+++ b/src/main/java/com/github/simiacryptus/aicoder/actions/MarkdownListAction.java
@@ -0,0 +1,94 @@
+package com.github.simiacryptus.aicoder.actions;
+
+import com.github.simiacryptus.aicoder.config.AppSettingsState;
+import com.github.simiacryptus.aicoder.openai.CompletionRequest;
+import com.github.simiacryptus.aicoder.util.psi.PsiUtil;
+import com.github.simiacryptus.aicoder.util.StringTools;
+import com.github.simiacryptus.aicoder.util.UITools;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.editor.Caret;
+import com.intellij.openapi.editor.Document;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static com.github.simiacryptus.aicoder.util.UITools.insertString;
+
+public class MarkdownListAction extends AnAction {
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ e.getPresentation().setEnabledAndVisible(isEnabled(e));
+ super.update(e);
+ }
+
+ private static boolean isEnabled(@NotNull AnActionEvent e) {
+ return null != getMarkdownListParams(e);
+ }
+
+ @Nullable
+ public static MarkdownListParams getMarkdownListParams(@NotNull AnActionEvent e) {
+ Caret caret = e.getData(CommonDataKeys.CARET);
+ if (null == caret) return null;
+ PsiFile psiFile = e.getData(CommonDataKeys.PSI_FILE);
+ if (null == psiFile) return null;
+ PsiElement list = PsiUtil.getSmallestIntersecting(psiFile, caret.getSelectionStart(), caret.getSelectionEnd(), "MarkdownListImpl");
+ if (null == list) return null;
+ return new MarkdownListParams(caret, list);
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent event) {
+ MarkdownListParams markdownListParams = getMarkdownListParams(event);
+ AppSettingsState settings = AppSettingsState.getInstance();
+ List items = StringTools.trim(PsiUtil.getAll(Objects.requireNonNull(markdownListParams).list, "MarkdownListItemImpl")
+ .stream().map(item -> PsiUtil.getAll(item, "MarkdownParagraphImpl").get(0).getText()).collect(Collectors.toList()), 10, false);
+ CharSequence indent = UITools.getIndent(markdownListParams.caret);
+ CharSequence n = Integer.toString(items.size() * 2);
+ int endOffset = markdownListParams.list.getTextRange().getEndOffset();
+ String listPrefix = "* ";
+ CompletionRequest completionRequest = settings.createTranslationRequest()
+ .setInstruction(UITools.getInstruction("List " + n + " items"))
+ .setInputType("instruction")
+ .setInputText("List " + n + " items")
+ .setOutputType("list")
+ .setOutputAttrute("style", settings.style)
+ .buildCompletionRequest()
+ .appendPrompt(items.stream().map(x2 -> listPrefix + x2).collect(Collectors.joining("\n")) + "\n" + listPrefix);
+ Document document = event.getRequiredData(CommonDataKeys.EDITOR).getDocument();
+ UITools.redoableRequest(completionRequest, "", event, newText -> transformCompletion(markdownListParams, indent, listPrefix, newText), newText -> insertString(document, endOffset, newText));
+ }
+
+ @NotNull
+ private static String transformCompletion(MarkdownListParams markdownListParams, CharSequence indent, String listPrefix, CharSequence complete) {
+ List newItems = Arrays.stream(complete.toString().split("\n")).map(String::trim)
+ .filter(x1 -> x1.length() > 0).map(x1 -> StringTools.stripPrefix(x1, listPrefix)).collect(Collectors.toList());
+ String strippedList = Arrays.stream(markdownListParams.list.getText().split("\n"))
+ .map(String::trim).filter(x -> x.length() > 0).collect(Collectors.joining("\n"));
+ String bulletString = Stream.of("- [ ] ", "- ", "* ")
+ .filter(strippedList::startsWith).findFirst().orElse("1. ");
+ CharSequence itemText = indent + newItems.stream().map(x -> bulletString + x)
+ .collect(Collectors.joining("\n" + indent));
+ return "\n" + itemText;
+ }
+
+ public static class MarkdownListParams {
+ public final Caret caret;
+ public final PsiElement list;
+
+ private MarkdownListParams(Caret caret, PsiElement list) {
+ this.caret = caret;
+ this.list = list;
+ }
+
+ }
+}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/actions/MarkdownNewTableColAction.java b/src/main/java/com/github/simiacryptus/aicoder/actions/MarkdownNewTableColAction.java
new file mode 100644
index 00000000..4b8b92c6
--- /dev/null
+++ b/src/main/java/com/github/simiacryptus/aicoder/actions/MarkdownNewTableColAction.java
@@ -0,0 +1,106 @@
+package com.github.simiacryptus.aicoder.actions;
+
+import com.github.simiacryptus.aicoder.config.AppSettingsState;
+import com.github.simiacryptus.aicoder.openai.CompletionRequest;
+import com.github.simiacryptus.aicoder.util.StringTools;
+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.actionSystem.CommonDataKeys;
+import com.intellij.openapi.editor.Caret;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static com.github.simiacryptus.aicoder.util.UITools.replaceString;
+
+public class MarkdownNewTableColAction extends AnAction {
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ e.getPresentation().setEnabledAndVisible(isEnabled(e));
+ super.update(e);
+ }
+
+ private static boolean isEnabled(@NotNull AnActionEvent e) {
+ return null != getMarkdownNewTableColParams(e);
+ }
+
+ @Nullable
+ public static MarkdownNewTableColParams getMarkdownNewTableColParams(@NotNull AnActionEvent e) {
+ Caret caret = e.getData(CommonDataKeys.CARET);
+ if (null == caret) return null;
+ PsiFile psiFile = e.getData(CommonDataKeys.PSI_FILE);
+ if (null == psiFile) return null;
+ PsiElement table = PsiUtil.getSmallestIntersecting(psiFile, caret.getSelectionStart(), caret.getSelectionEnd(), "MarkdownTableImpl");
+ if (null == table) return null;
+ List rows = Arrays.asList(StringTools.transposeMarkdownTable(PsiUtil.getAll(table, "MarkdownTableRowImpl")
+ .stream().map(PsiElement::getText).collect(Collectors.joining("\n")), false, false).split("\n"));
+ CharSequence n = Integer.toString(rows.size() * 2);
+ return new MarkdownNewTableColParams(caret, table, rows, n);
+ }
+
+ @NotNull
+ public static CompletionRequest newRowsRequest(@NotNull AppSettingsState settings, CharSequence n, @NotNull List rows, CharSequence rowPrefix) {
+ return settings.createTranslationRequest()
+ .setInstruction(UITools.getInstruction("List " + n + " items"))
+ .setInputType("instruction")
+ .setInputText("List " + n + " items")
+ .setOutputType("markdown")
+ .setOutputAttrute("style", settings.style)
+ .buildCompletionRequest()
+ .appendPrompt("\n" + String.join("\n", rows) + "\n" + rowPrefix);
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent event) {
+ MarkdownNewTableColParams markdownNewTableColParams = getMarkdownNewTableColParams(event);
+ AppSettingsState settings = AppSettingsState.getInstance();
+ CharSequence columnName = JOptionPane.showInputDialog(null, "Column Name:", "Add Column", JOptionPane.QUESTION_MESSAGE).trim();
+ CompletionRequest request = newRowsRequest(settings, Objects.requireNonNull(markdownNewTableColParams).n, markdownNewTableColParams.rows, "| " + columnName + " | ");
+ Document document = event.getRequiredData(CommonDataKeys.EDITOR).getDocument();
+ TextRange textRange = markdownNewTableColParams.table.getTextRange();
+ int startOffset = textRange.getStartOffset();
+ int endOffset = textRange.getEndOffset();
+ UITools.redoableRequest(request, "", event,
+ newText -> transformCompletion(markdownNewTableColParams, newText, columnName),
+ newText -> replaceString(document, startOffset, endOffset, newText));
+ }
+
+ @NotNull
+ private static String transformCompletion(MarkdownNewTableColParams markdownNewTableColParams, CharSequence complete, CharSequence columnName) {
+ complete = "| " + columnName + " | " + complete;
+ CharSequence indent = UITools.getIndent(markdownNewTableColParams.caret);
+ List newRows = Arrays.stream(("" + complete).split("\n"))
+ .map(String::trim).filter(x -> x.length() > 0).collect(Collectors.toList());
+ String newTableTxt = StringTools.transposeMarkdownTable(Stream.concat(markdownNewTableColParams.rows.stream(),
+ newRows.stream()).collect(Collectors.joining("\n")), false, true);
+ return newTableTxt.replace("\n", "\n" + indent);
+ }
+
+ public static class MarkdownNewTableColParams {
+ public final Caret caret;
+ public final PsiElement table;
+ public final List rows;
+ public final CharSequence n;
+
+ private MarkdownNewTableColParams(Caret caret, PsiElement table, List rows, CharSequence n) {
+ this.caret = caret;
+ this.table = table;
+ this.rows = rows;
+ this.n = n;
+ }
+
+ }
+}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/actions/MarkdownNewTableColsAction.java b/src/main/java/com/github/simiacryptus/aicoder/actions/MarkdownNewTableColsAction.java
new file mode 100644
index 00000000..f5a17c7e
--- /dev/null
+++ b/src/main/java/com/github/simiacryptus/aicoder/actions/MarkdownNewTableColsAction.java
@@ -0,0 +1,89 @@
+package com.github.simiacryptus.aicoder.actions;
+
+import com.github.simiacryptus.aicoder.config.AppSettingsState;
+import com.github.simiacryptus.aicoder.openai.CompletionRequest;
+import com.github.simiacryptus.aicoder.util.psi.PsiUtil;
+import com.github.simiacryptus.aicoder.util.StringTools;
+import com.github.simiacryptus.aicoder.util.UITools;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.editor.Caret;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static com.github.simiacryptus.aicoder.util.UITools.replaceString;
+
+public class MarkdownNewTableColsAction extends AnAction {
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ e.getPresentation().setEnabledAndVisible(isEnabled(e));
+ super.update(e);
+ }
+
+ private static boolean isEnabled(@NotNull AnActionEvent e) {
+ return null != getMarkdownNewTableColsParams(e);
+ }
+
+ @Nullable
+ public static MarkdownNewTableColsParams getMarkdownNewTableColsParams(@NotNull AnActionEvent e) {
+ Caret caret = e.getData(CommonDataKeys.CARET);
+ if (null != caret) {
+ PsiFile psiFile = e.getData(CommonDataKeys.PSI_FILE);
+ if (null != psiFile) {
+ PsiElement table = PsiUtil.getSmallestIntersecting(psiFile, caret.getSelectionStart(), caret.getSelectionEnd(), "MarkdownTableImpl");
+ if (null != table) {
+ List rows = Arrays.asList(StringTools.transposeMarkdownTable(PsiUtil.getAll(table, "MarkdownTableRowImpl")
+ .stream().map(PsiElement::getText).collect(Collectors.joining("\n")), false, false).split("\n"));
+ CharSequence n = Integer.toString(rows.size() * 2);
+ return new MarkdownNewTableColsParams(caret, table, rows, n);
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent event) {
+ MarkdownNewTableColsParams markdownNewTableColsParams = getMarkdownNewTableColsParams(event);
+ AppSettingsState settings = AppSettingsState.getInstance();
+ CharSequence indent = UITools.getIndent(Objects.requireNonNull(markdownNewTableColsParams).caret);
+ CompletionRequest request = MarkdownNewTableColAction.newRowsRequest(settings, markdownNewTableColsParams.n, markdownNewTableColsParams.rows, "");
+ UITools.redoableRequest(request, "", event, newText -> transformCompletion(markdownNewTableColsParams, indent, newText), newText -> replaceString(event.getRequiredData(CommonDataKeys.EDITOR).getDocument(),
+ markdownNewTableColsParams.table.getTextRange().getStartOffset(),
+ markdownNewTableColsParams.table.getTextRange().getEndOffset(), newText));
+ }
+
+ @NotNull
+ private static String transformCompletion(MarkdownNewTableColsParams markdownNewTableColsParams, CharSequence indent, CharSequence complete) {
+ List newRows = Arrays.stream(("" + complete).split("\n")).map(String::trim)
+ .filter(x -> x.length() > 0).collect(Collectors.toList());
+ String newTableTxt = StringTools.transposeMarkdownTable(Stream.concat(markdownNewTableColsParams.rows.stream(), newRows.stream())
+ .collect(Collectors.joining("\n")), false, true);
+ return newTableTxt.replace("\n", "\n" + indent);
+ }
+
+ public static class MarkdownNewTableColsParams {
+ public final Caret caret;
+ public final PsiElement table;
+ public final List rows;
+ public final CharSequence n;
+
+ private MarkdownNewTableColsParams(Caret caret, PsiElement table, List rows, CharSequence n) {
+ this.caret = caret;
+ this.table = table;
+ this.rows = rows;
+ this.n = n;
+ }
+
+ }
+}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/actions/MarkdownNewTableRowsAction.java b/src/main/java/com/github/simiacryptus/aicoder/actions/MarkdownNewTableRowsAction.java
new file mode 100644
index 00000000..c6ae5edd
--- /dev/null
+++ b/src/main/java/com/github/simiacryptus/aicoder/actions/MarkdownNewTableRowsAction.java
@@ -0,0 +1,83 @@
+package com.github.simiacryptus.aicoder.actions;
+
+import com.github.simiacryptus.aicoder.config.AppSettingsState;
+import com.github.simiacryptus.aicoder.util.StringTools;
+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.actionSystem.CommonDataKeys;
+import com.intellij.openapi.editor.Caret;
+import com.intellij.openapi.editor.Document;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static com.github.simiacryptus.aicoder.util.UITools.insertString;
+
+public class MarkdownNewTableRowsAction extends AnAction {
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ e.getPresentation().setEnabledAndVisible(isEnabled(e));
+ super.update(e);
+ }
+
+ private static boolean isEnabled(@NotNull AnActionEvent e) {
+ return null != getMarkdownNewTableRowsParams(e);
+ }
+
+ @Nullable
+ public static MarkdownNewTableRowsParams getMarkdownNewTableRowsParams(@NotNull AnActionEvent e) {
+ Caret caret = e.getData(CommonDataKeys.CARET);
+ if (null != caret) {
+ PsiFile psiFile = e.getData(CommonDataKeys.PSI_FILE);
+ if (null != psiFile) {
+ PsiElement table = PsiUtil.getSmallestIntersecting(psiFile, caret.getSelectionStart(), caret.getSelectionEnd(), "MarkdownTableImpl");
+ if (null != table) {
+ return new MarkdownNewTableRowsParams(caret, table);
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent event) {
+ MarkdownNewTableRowsParams markdownNewTableRowsParams = getMarkdownNewTableRowsParams(event);
+ List rows = StringTools.trim(PsiUtil.getAll(Objects.requireNonNull(markdownNewTableRowsParams).table, "MarkdownTableRowImpl")
+ .stream().map(PsiElement::getText).collect(Collectors.toList()), 10, true);
+ CharSequence n = Integer.toString(rows.size() * 2);
+ AppSettingsState settings = AppSettingsState.getInstance();
+ int endOffset = markdownNewTableRowsParams.table.getTextRange().getEndOffset();
+ Document document = event.getRequiredData(CommonDataKeys.EDITOR).getDocument();
+ UITools.redoableRequest(MarkdownNewTableColAction.newRowsRequest(settings, n, rows, ""), "", event,
+ newText -> transformCompletion(markdownNewTableRowsParams, newText),
+ newText -> insertString(document, endOffset, newText));
+ }
+
+ @NotNull
+ private static String transformCompletion(MarkdownNewTableRowsParams markdownNewTableRowsParams, CharSequence complete) {
+ CharSequence indent = UITools.getIndent(markdownNewTableRowsParams.caret);
+ List newRows = Arrays.stream(("" + complete).split("\n"))
+ .map(String::trim).filter(x -> x.length() > 0).collect(Collectors.toList());
+ return "\n" + indent + newRows.stream().collect(Collectors.joining("\n" + indent));
+ }
+
+ public static class MarkdownNewTableRowsParams {
+ public final Caret caret;
+ public final PsiElement table;
+
+ private MarkdownNewTableRowsParams(Caret caret, PsiElement table) {
+ this.caret = caret;
+ this.table = table;
+ }
+
+ }
+}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/actions/PasteAction.java b/src/main/java/com/github/simiacryptus/aicoder/actions/PasteAction.java
new file mode 100644
index 00000000..f214fef6
--- /dev/null
+++ b/src/main/java/com/github/simiacryptus/aicoder/actions/PasteAction.java
@@ -0,0 +1,67 @@
+package com.github.simiacryptus.aicoder.actions;
+
+import com.github.simiacryptus.aicoder.util.ComputerLanguage;
+import com.github.simiacryptus.aicoder.config.AppSettingsState;
+import com.github.simiacryptus.aicoder.openai.CompletionRequest;
+import com.github.simiacryptus.aicoder.util.UITools;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.editor.Caret;
+import com.intellij.openapi.editor.CaretModel;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.ide.CopyPasteManager;
+import org.jetbrains.annotations.NotNull;
+
+import java.awt.datatransfer.DataFlavor;
+import java.util.Objects;
+
+import static com.github.simiacryptus.aicoder.util.UITools.insertString;
+import static com.github.simiacryptus.aicoder.util.UITools.replaceString;
+
+public class PasteAction extends AnAction {
+
+ public PasteAction() {
+ super("", "Paste", null);
+ }
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ e.getPresentation().setEnabledAndVisible(isEnabled(e));
+ super.update(e);
+ }
+
+ private static boolean isEnabled(@NotNull AnActionEvent e) {
+ if(CopyPasteManager.getInstance().getContents(DataFlavor.stringFlavor) == null) return false;
+ return null != ComputerLanguage.getComputerLanguage(e);
+ }
+
+ @Override
+ public void actionPerformed(@NotNull final AnActionEvent event) {
+ final Editor editor = event.getRequiredData(CommonDataKeys.EDITOR);
+ final CaretModel caretModel = editor.getCaretModel();
+ final Caret primaryCaret = caretModel.getPrimaryCaret();
+ int selectionStart = primaryCaret.getSelectionStart();
+ int selectionEnd = primaryCaret.getSelectionEnd();
+ String selectedText = primaryCaret.getSelectedText();
+ String language = Objects.requireNonNull(ComputerLanguage.getComputerLanguage(event)).name();
+ String text = Objects.requireNonNull(CopyPasteManager.getInstance().getContents(DataFlavor.stringFlavor)).toString().trim();
+ CompletionRequest request = AppSettingsState.getInstance().createTranslationRequest()
+ .setInputType("source")
+ .setOutputType("translated")
+ .setInstruction("Translate this input into " + language)
+ .setInputAttribute("language", "autodetect")
+ .setOutputAttrute("language", language)
+ .setInputText(text)
+ .buildCompletionRequest();
+ Caret caret = event.getData(CommonDataKeys.CARET);
+ CharSequence indent = UITools.getIndent(caret);
+ UITools.redoableRequest(request, indent, event, newText -> {
+ if(selectedText == null) {
+ return insertString(editor.getDocument(), selectionStart, newText);
+ } else {
+ return replaceString(editor.getDocument(), selectionStart, selectionEnd, newText);
+ }
+ });
+ }
+}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/actions/PrintTreeAction.java b/src/main/java/com/github/simiacryptus/aicoder/actions/PrintTreeAction.java
new file mode 100644
index 00000000..d3cdf1f0
--- /dev/null
+++ b/src/main/java/com/github/simiacryptus/aicoder/actions/PrintTreeAction.java
@@ -0,0 +1,31 @@
+package com.github.simiacryptus.aicoder.actions;
+
+import com.github.simiacryptus.aicoder.config.AppSettingsState;
+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;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+
+public class PrintTreeAction extends AnAction {
+ public static final Logger log = Logger.getInstance(PrintTreeAction.class);
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ e.getPresentation().setEnabledAndVisible(isEnabled(e));
+ super.update(e);
+ }
+
+ private static boolean isEnabled(@NotNull AnActionEvent e) {
+ if(!AppSettingsState.getInstance().devActions) return false;
+ return null != PsiUtil.getPsiFile(e);
+ }
+
+ @Override
+ public void actionPerformed(@NotNull final AnActionEvent e1) {
+ log.warn(PsiUtil.printTree(Objects.requireNonNull(PsiUtil.getPsiFile(e1))));
+ }
+
+}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/actions/PsiClassContextAction.java b/src/main/java/com/github/simiacryptus/aicoder/actions/PsiClassContextAction.java
new file mode 100644
index 00000000..d5f3d6d5
--- /dev/null
+++ b/src/main/java/com/github/simiacryptus/aicoder/actions/PsiClassContextAction.java
@@ -0,0 +1,110 @@
+package com.github.simiacryptus.aicoder.actions;
+
+import com.github.simiacryptus.aicoder.openai.CompletionRequest;
+import com.github.simiacryptus.aicoder.util.ComputerLanguage;
+import com.github.simiacryptus.aicoder.config.AppSettingsState;
+import com.github.simiacryptus.aicoder.util.psi.PsiClassContext;
+import com.github.simiacryptus.aicoder.util.psi.PsiUtil;
+import com.github.simiacryptus.aicoder.util.UITools;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.editor.Caret;
+import com.intellij.openapi.editor.CaretModel;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+import java.util.Optional;
+
+import static com.github.simiacryptus.aicoder.util.UITools.insertString;
+
+public class PsiClassContextAction extends AnAction {
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ e.getPresentation().setEnabledAndVisible(isEnabled(e));
+ super.update(e);
+ }
+
+ private static boolean isEnabled(@NotNull AnActionEvent e) {
+ return getPsiClassContextActionParams(e).isPresent();
+ }
+
+ public static @NotNull Optional getPsiClassContextActionParams(@NotNull AnActionEvent e) {
+ PsiFile psiFile = e.getData(CommonDataKeys.PSI_FILE);
+ if (null != psiFile) {
+ Caret caret = e.getData(CommonDataKeys.CARET);
+ if (null != caret) {
+ int selectionStart = caret.getSelectionStart();
+ int selectionEnd = caret.getSelectionEnd();
+ PsiElement largestIntersectingComment = PsiUtil.getLargestIntersectingComment(psiFile, selectionStart, selectionEnd);
+ if (largestIntersectingComment != null) {
+ return Optional.of(new PsiClassContextActionParams(psiFile, caret, selectionStart, selectionEnd, largestIntersectingComment));
+ }
+ }
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public void actionPerformed(@NotNull final AnActionEvent event) {
+ String humanLanguage = AppSettingsState.getInstance().humanLanguage;
+ ComputerLanguage computerLanguage = ComputerLanguage.getComputerLanguage(event);
+ PsiClassContextActionParams psiClassContextActionParams = getPsiClassContextActionParams(event).get();
+ final Editor editor = event.getRequiredData(CommonDataKeys.EDITOR);
+ final CaretModel caretModel = editor.getCaretModel();
+ final Caret primaryCaret = caretModel.getPrimaryCaret();
+ AppSettingsState settings = AppSettingsState.getInstance();
+
+ String instruct = psiClassContextActionParams.largestIntersectingComment.getText().trim();
+ if (primaryCaret.getSelectionEnd() > primaryCaret.getSelectionStart()) {
+ @NotNull String selectedText = Objects.requireNonNull(primaryCaret.getSelectedText());
+ if (Objects.requireNonNull(selectedText).split(" ").length > 4) {
+ instruct = selectedText.trim();
+ }
+ }
+ assert computerLanguage != null;
+ String specification = Objects.requireNonNull(computerLanguage.getCommentModel(instruct)).fromString(instruct).stream()
+ .map(Object::toString)
+ .map(String::trim)
+ .filter(x -> !x.isEmpty())
+ .reduce((a, b) -> a + " " + b).get();
+ int endOffset = psiClassContextActionParams.largestIntersectingComment.getTextRange().getEndOffset();
+ CompletionRequest request = settings.createTranslationRequest()
+ .setInstruction("Implement " + humanLanguage + " as " + computerLanguage.name() + " code")
+ .setInputType(humanLanguage)
+ .setInputAttribute("type", "instruction")
+ .setInputText(specification)
+ .setOutputType(computerLanguage.name())
+ .setOutputAttrute("type", "code")
+ .setOutputAttrute("style", settings.style)
+ .buildCompletionRequest()
+ .appendPrompt(PsiClassContext.getContext(psiClassContextActionParams.psiFile, psiClassContextActionParams.selectionStart, psiClassContextActionParams.selectionEnd) + "\n");
+ UITools.redoableRequest(request, UITools.getIndent(psiClassContextActionParams.caret), event, newText -> transformCompletion(newText), newText -> insertString(editor.getDocument(), endOffset, newText));
+ }
+
+ @NotNull
+ private static String transformCompletion(CharSequence result) {
+ return "\n" + result;
+ }
+
+ public static class PsiClassContextActionParams {
+ public final PsiFile psiFile;
+ public final Caret caret;
+ public final int selectionStart;
+ public final int selectionEnd;
+ public final PsiElement largestIntersectingComment;
+
+ private PsiClassContextActionParams(PsiFile psiFile, Caret caret, int selectionStart, int selectionEnd, PsiElement largestIntersectingComment) {
+ this.psiFile = psiFile;
+ this.caret = caret;
+ this.selectionStart = selectionStart;
+ this.selectionEnd = selectionEnd;
+ this.largestIntersectingComment = largestIntersectingComment;
+ }
+
+ }
+}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/actions/RecentEditsAction.java b/src/main/java/com/github/simiacryptus/aicoder/actions/RecentEditsAction.java
new file mode 100644
index 00000000..09cc4ba3
--- /dev/null
+++ b/src/main/java/com/github/simiacryptus/aicoder/actions/RecentEditsAction.java
@@ -0,0 +1,78 @@
+package com.github.simiacryptus.aicoder.actions;
+
+import com.github.simiacryptus.aicoder.config.AppSettingsState;
+import com.github.simiacryptus.aicoder.openai.CompletionRequest;
+import com.github.simiacryptus.aicoder.util.ComputerLanguage;
+import com.github.simiacryptus.aicoder.util.IndentedText;
+import com.github.simiacryptus.aicoder.util.UITools;
+import com.intellij.openapi.actionSystem.ActionGroup;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.editor.Caret;
+import com.intellij.openapi.editor.CaretModel;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+
+import static com.github.simiacryptus.aicoder.util.UITools.replaceString;
+import static java.util.Objects.requireNonNull;
+
+public class RecentEditsAction extends ActionGroup {
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ e.getPresentation().setEnabledAndVisible(isEnabled(e));
+ super.update(e);
+ }
+
+ private static boolean isEnabled(@NotNull AnActionEvent e) {
+ return null != ComputerLanguage.getComputerLanguage(e);
+ }
+
+ @Override
+ public AnAction @NotNull [] getChildren(@Nullable AnActionEvent event) {
+ assert event != null;
+ String computerLanguage = requireNonNull(ComputerLanguage.getComputerLanguage(event)).name();
+ ArrayList children = new ArrayList<>();
+ for (String instruction : AppSettingsState.getInstance().getEditHistory()) {
+ int id = children.size() + 1;
+ String text;
+ if(id<10) {
+ text = String.format("_%d: %s", id, instruction);
+ } else {
+ text = String.format("%d: %s", id, instruction);
+ }
+ children.add(new AnAction(text, instruction, null) {
+ @Override
+ public void actionPerformed(@NotNull final AnActionEvent event1) {
+ final Editor editor = event1.getRequiredData(CommonDataKeys.EDITOR);
+ final CaretModel caretModel = editor.getCaretModel();
+ final Caret primaryCaret = caretModel.getPrimaryCaret();
+ int selectionStart = primaryCaret.getSelectionStart();
+ int selectionEnd = primaryCaret.getSelectionEnd();
+ String selectedText = primaryCaret.getSelectedText();
+ AppSettingsState settings = AppSettingsState.getInstance();
+ settings.addInstructionToHistory(instruction);
+ CompletionRequest request = settings.createTranslationRequest()
+ .setInputType(computerLanguage)
+ .setOutputType(computerLanguage)
+ .setInstruction(instruction)
+ .setInputAttribute("type", "before")
+ .setOutputAttrute("type", "after")
+ .setInputText(IndentedText.fromString(selectedText).getTextBlock())
+ .buildCompletionRequest();
+ Caret caret = event1.getData(CommonDataKeys.CARET);
+ CharSequence indent = UITools.getIndent(caret);
+ Document document = editor.getDocument();
+ UITools.redoableRequest(request, indent, event1,
+ newText -> replaceString(document, selectionStart, selectionEnd, newText));
+ }
+ });
+ }
+ return children.toArray(AnAction[]::new);
+ }
+}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/actions/RedoLast.java b/src/main/java/com/github/simiacryptus/aicoder/actions/RedoLast.java
new file mode 100644
index 00000000..2f05bb3e
--- /dev/null
+++ b/src/main/java/com/github/simiacryptus/aicoder/actions/RedoLast.java
@@ -0,0 +1,29 @@
+package com.github.simiacryptus.aicoder.actions;
+
+import com.github.simiacryptus.aicoder.util.UITools;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import org.jetbrains.annotations.NotNull;
+
+public class RedoLast extends AnAction {
+
+ public RedoLast() {
+ super("_Redo Last", "Redo last", null);
+ }
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ e.getPresentation().setEnabledAndVisible(isEnabled(e));
+ super.update(e);
+ }
+
+ private static boolean isEnabled(@NotNull AnActionEvent e) {
+ return null != UITools.retry.get(e.getRequiredData(CommonDataKeys.EDITOR).getDocument());
+ }
+
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ UITools.retry.get(e.getRequiredData(CommonDataKeys.EDITOR).getDocument()).run();
+ }
+}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/actions/RewordCommentAction.java b/src/main/java/com/github/simiacryptus/aicoder/actions/RewordCommentAction.java
new file mode 100644
index 00000000..959feeb9
--- /dev/null
+++ b/src/main/java/com/github/simiacryptus/aicoder/actions/RewordCommentAction.java
@@ -0,0 +1,98 @@
+package com.github.simiacryptus.aicoder.actions;
+
+import com.github.simiacryptus.aicoder.openai.CompletionRequest;
+import com.github.simiacryptus.aicoder.util.ComputerLanguage;
+import com.github.simiacryptus.aicoder.config.AppSettingsState;
+import com.github.simiacryptus.aicoder.util.psi.PsiUtil;
+import com.github.simiacryptus.aicoder.util.StringTools;
+import com.github.simiacryptus.aicoder.util.TextBlockFactory;
+import com.github.simiacryptus.aicoder.util.UITools;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.editor.Caret;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Objects;
+
+import static com.github.simiacryptus.aicoder.util.UITools.replaceString;
+
+public class RewordCommentAction extends AnAction {
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ e.getPresentation().setEnabledAndVisible(isEnabled(e));
+ super.update(e);
+ }
+
+ private static boolean isEnabled(@NotNull AnActionEvent e) {
+ return null != getRewordCommentParams(e);
+ }
+
+ @Nullable
+ public static RewordCommentParams getRewordCommentParams(@NotNull AnActionEvent e) {
+ PsiFile psiFile = e.getData(CommonDataKeys.PSI_FILE);
+ if (null == psiFile) return null;
+ Caret caret = e.getData(CommonDataKeys.CARET);
+ if (null == caret) return null;
+ int selectionStart = caret.getSelectionStart();
+ int selectionEnd = caret.getSelectionEnd();
+ PsiElement largestIntersectingComment = PsiUtil.getLargestIntersectingComment(psiFile, selectionStart, selectionEnd);
+ if (largestIntersectingComment == null) return null;
+ return new RewordCommentParams(caret, largestIntersectingComment);
+ }
+
+ @Override
+ public void actionPerformed(@NotNull final AnActionEvent event) {
+ String humanLanguage = AppSettingsState.getInstance().humanLanguage;
+ ComputerLanguage computerLanguage = ComputerLanguage.getComputerLanguage(event);
+ RewordCommentParams rewordCommentParams = getRewordCommentParams(event);
+ final Editor editor = event.getRequiredData(CommonDataKeys.EDITOR);
+ AppSettingsState settings = AppSettingsState.getInstance();
+ String text = Objects.requireNonNull(rewordCommentParams).largestIntersectingComment.getText();
+ TextBlockFactory> commentModel = Objects.requireNonNull(computerLanguage).getCommentModel(text);
+ String commentText = Objects.requireNonNull(commentModel).fromString(text.trim()).stream()
+ .map(Object::toString)
+ .map(String::trim)
+ .filter(x -> !x.isEmpty())
+ .reduce((a, b) -> a + "\n" + b).get();
+ int startOffset = rewordCommentParams.largestIntersectingComment.getTextRange().getStartOffset();
+ int endOffset = rewordCommentParams.largestIntersectingComment.getTextRange().getEndOffset();
+ CharSequence indent = UITools.getIndent(rewordCommentParams.caret);
+ CompletionRequest request = settings.createTranslationRequest()
+ .setInstruction(UITools.getInstruction("Reword"))
+ .setInputText(commentText)
+ .setInputType(humanLanguage)
+ .setOutputAttrute("type", "input")
+ .setOutputType(humanLanguage)
+ .setOutputAttrute("type", "output")
+ .setOutputAttrute("style", settings.style)
+ .buildCompletionRequest();
+ Document document = editor.getDocument();
+ UITools.redoableRequest(request, "", event,
+ newText -> transformCompletion(commentModel, indent, newText),
+ newText -> replaceString(document, startOffset, endOffset, newText));
+ }
+
+ @NotNull
+ private static CharSequence transformCompletion(TextBlockFactory> commentModel, CharSequence indent, CharSequence result) {
+ String lineWrapping = StringTools.lineWrapping(result, 120);
+ return indent.toString() + commentModel.fromString(lineWrapping).withIndent(indent);
+ }
+
+ public static class RewordCommentParams {
+ public final Caret caret;
+ public final PsiElement largestIntersectingComment;
+
+ private RewordCommentParams(Caret caret, PsiElement largestIntersectingComment) {
+ this.caret = caret;
+ this.largestIntersectingComment = largestIntersectingComment;
+ }
+
+ }
+}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/actions/ToHumanLanguageAction.java b/src/main/java/com/github/simiacryptus/aicoder/actions/ToHumanLanguageAction.java
new file mode 100644
index 00000000..bf9b3722
--- /dev/null
+++ b/src/main/java/com/github/simiacryptus/aicoder/actions/ToHumanLanguageAction.java
@@ -0,0 +1,60 @@
+package com.github.simiacryptus.aicoder.actions;
+
+import com.github.simiacryptus.aicoder.util.ComputerLanguage;
+import com.github.simiacryptus.aicoder.config.AppSettingsState;
+import com.github.simiacryptus.aicoder.openai.CompletionRequest;
+import com.github.simiacryptus.aicoder.util.UITools;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.editor.Caret;
+import com.intellij.openapi.editor.CaretModel;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import org.jetbrains.annotations.NotNull;
+
+import static com.github.simiacryptus.aicoder.util.UITools.replaceString;
+import static java.util.Objects.requireNonNull;
+
+public class ToHumanLanguageAction extends AnAction {
+
+ public ToHumanLanguageAction() {
+ super("", "", null);
+ }
+
+ @Override
+ public void update(@NotNull AnActionEvent e) {
+ e.getPresentation().setEnabledAndVisible(isEnabled(e));
+ super.update(e);
+ }
+
+ private static boolean isEnabled(@NotNull AnActionEvent e) {
+ return null != ComputerLanguage.getComputerLanguage(e);
+ }
+
+ @Override
+ public void actionPerformed(@NotNull final AnActionEvent event) {
+ final Editor editor = event.getRequiredData(CommonDataKeys.EDITOR);
+ final CaretModel caretModel = editor.getCaretModel();
+ final Caret primaryCaret = caretModel.getPrimaryCaret();
+ int selectionStart = primaryCaret.getSelectionStart();
+ int selectionEnd = primaryCaret.getSelectionEnd();
+ String selectedText = primaryCaret.getSelectedText();
+ ComputerLanguage language = ComputerLanguage.getComputerLanguage(event);
+ String computerLanguage = requireNonNull(language).name();
+ AppSettingsState settings = AppSettingsState.getInstance();
+ CompletionRequest request = settings.createTranslationRequest()
+ .setInstruction(UITools.getInstruction("Describe this code"))
+ .setInputText(selectedText)
+ .setInputType(computerLanguage)
+ .setInputAttribute("type", "input")
+ .setOutputType(AppSettingsState.getInstance().humanLanguage.toLowerCase())
+ .setOutputAttrute("type", "output")
+ .setOutputAttrute("style", settings.style)
+ .buildCompletionRequest();
+ Caret caret = event.getData(CommonDataKeys.CARET);
+ CharSequence indent = UITools.getIndent(caret);
+ Document document = editor.getDocument();
+ UITools.redoableRequest(request, indent, event, newText -> replaceString(document, selectionStart, selectionEnd, newText));
+ }
+}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/config/AppSettingsComponent.java b/src/main/java/com/github/simiacryptus/aicoder/config/AppSettingsComponent.java
index 105405a5..c8b8936f 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/config/AppSettingsComponent.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/config/AppSettingsComponent.java
@@ -1,6 +1,5 @@
package com.github.simiacryptus.aicoder.config;
-import com.fasterxml.jackson.databind.JsonNode;
import com.github.simiacryptus.aicoder.util.StyleUtil;
import com.github.simiacryptus.aicoder.openai.OpenAI_API;
import com.intellij.openapi.diagnostic.Logger;
@@ -15,79 +14,68 @@
import java.awt.event.ActionEvent;
import java.util.Arrays;
-public class AppSettingsComponent extends SimpleSettingsComponent {
+public class AppSettingsComponent {
private static final Logger log = Logger.getInstance(AppSettingsComponent.class);
- @Name("API Base")
- public final JBTextField apiBase = new JBTextField();
- @Name("API Key")
- public final JBPasswordField apiKey = new JBPasswordField();
- @Name("Model")
- public final JComponent model = getModelSelector();
-
- @NotNull
- private static JComponent getModelSelector() {
- AppSettingsState settings = AppSettingsState.getInstance();
- CharSequence apiKey = settings.apiKey;
- if (null != apiKey && apiKey.toString().trim().length() > 0) {
- try {
- ComboBox comboBox = new ComboBox<>(new CharSequence[]{settings.model});
- OpenAI_API.onSuccess(OpenAI_API.INSTANCE.getEngines(), engines -> {
- JsonNode data = engines.get("data");
- CharSequence[] items = new CharSequence[data.size()];
- for (int i = 0; i < data.size(); i++) {
- items[i] = data.get(i).get("id").asText();
- }
- Arrays.sort(items);
- Arrays.stream(items).forEach(comboBox::addItem);
- });
- return comboBox;
- } catch (Throwable e) {
- log.warn(e);
- }
- }
- return new JBTextField();
- }
@Name("Style")
public final JBTextField style = new JBTextField();
- @Name("Human Language")
- public final JBTextField humanLanguage = new JBTextField();
- @Name("Max Prompt (Characters)")
- public final JBTextField maxPrompt = new JBTextField();
- @Name("Max Tokens")
- public final JBTextField maxTokens = new JBTextField();
- @Name("History Limit")
- public final JBTextField historyLimit = new JBTextField();
- @Name("Temperature")
- public final JBTextField temperature = new JBTextField();
+ @SuppressWarnings("unused")
public final JButton randomizeStyle = new JButton(new AbstractAction("Randomize Style") {
@Override
public void actionPerformed(ActionEvent e) {
style.setText(StyleUtil.randomStyle());
}
});
+ @SuppressWarnings("unused")
public final JButton testStyle = new JButton(new AbstractAction("Test Style") {
@Override
public void actionPerformed(ActionEvent e) {
StyleUtil.demoStyle(style.getText());
}
});
+
@Name("Token Counter")
public final JBTextField tokenCounter = new JBTextField();
+ @SuppressWarnings("unused")
public final JButton clearCounter = new JButton(new AbstractAction("Clear Token Counter") {
@Override
public void actionPerformed(ActionEvent e) {
tokenCounter.setText("0");
}
});
+
+ @SuppressWarnings("unused")
+ @Name("Human Language")
+ public final JBTextField humanLanguage = new JBTextField();
+ @SuppressWarnings("unused")
+ @Name("History Limit")
+ public final JBTextField historyLimit = new JBTextField();
+ @SuppressWarnings("unused")
@Name("Developer Tools")
public final JBCheckBox devActions = new JBCheckBox();
+ @SuppressWarnings("unused")
@Name("API Log Level")
- public final ComboBox apiLogLevel = new ComboBox(Arrays.stream(LogLevel.values()).map(x -> x.name()).toArray(CharSequence[]::new));
+ public final ComboBox apiLogLevel = new ComboBox<>(Arrays.stream(LogLevel.values()).map(Enum::name).toArray(String[]::new));
+
+ @SuppressWarnings("unused")
+ @Name("Temperature")
+ public final JBTextField temperature = new JBTextField();
+ @SuppressWarnings("unused")
+ @Name("Max Tokens")
+ public final JBTextField maxTokens = new JBTextField();
+ @SuppressWarnings("unused")
+ @Name("Max Prompt (Characters)")
+ public final JBTextField maxPrompt = new JBTextField();
+ @SuppressWarnings("unused")
+ @Name("Model")
+ public final JComponent model = OpenAI_API.INSTANCE.getModelSelector();
-// @Name("API Envelope")
-// public final ComboBox translationRequestTemplate = new ComboBox(Arrays.stream(TranslationRequestTemplate.values()).map(x->x.name()).toArray(String[]::new));
+ @Name("API Key")
+ public final JBPasswordField apiKey = new JBPasswordField();
+ @SuppressWarnings("unused")
+ @Name("API Base")
+ public final JBTextField apiBase = new JBTextField();
public AppSettingsComponent() {
tokenCounter.setEditable(false);
diff --git a/src/main/java/com/github/simiacryptus/aicoder/config/AppSettingsConfigurable.java b/src/main/java/com/github/simiacryptus/aicoder/config/AppSettingsConfigurable.java
index 80ad3a45..7c600272 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/config/AppSettingsConfigurable.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/config/AppSettingsConfigurable.java
@@ -1,11 +1,14 @@
package com.github.simiacryptus.aicoder.config;
+import com.github.simiacryptus.aicoder.util.UITools;
import com.intellij.openapi.options.Configurable;
+import com.intellij.util.ui.FormBuilder;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
+import java.util.Objects;
/**
* Provides controller functionality for application settings.
@@ -13,6 +16,7 @@
public class AppSettingsConfigurable implements Configurable {
@Nullable AppSettingsComponent settingsComponent;
+ private volatile @Nullable JPanel mainPanel = null;
public AppSettingsConfigurable() {
// A default constructor with no arguments is required because this implementation is registered as an applicationConfigurable EP
@@ -26,31 +30,46 @@ public AppSettingsConfigurable() {
@Override
public JComponent getPreferredFocusedComponent() {
- return settingsComponent.getPreferredFocusedComponent();
+ return Objects.requireNonNull(settingsComponent).getPreferredFocusedComponent();
}
@Nullable
@Override
public JComponent createComponent() {
- settingsComponent = new AppSettingsComponent();
- return settingsComponent.getPanel();
+ if (null == mainPanel) {
+ synchronized (this) {
+ if (null == mainPanel) {
+ FormBuilder formBuilder = FormBuilder.createFormBuilder();
+ settingsComponent = new AppSettingsComponent();
+ UITools.addFields(settingsComponent, formBuilder);
+ mainPanel = formBuilder.addComponentFillVertically(new JPanel(), 0).getPanel();
+ }
+ }
+ }
+ return mainPanel;
}
@Override
public boolean isModified() {
AppSettingsState buffer = new AppSettingsState();
- this.settingsComponent.getProperties(buffer);
+ if (this.settingsComponent != null) {
+ UITools.readUI(this.settingsComponent, buffer);
+ }
return !buffer.equals(AppSettingsState.getInstance());
}
@Override
public void apply() {
- this.settingsComponent.getProperties(AppSettingsState.getInstance());
+ if (this.settingsComponent != null) {
+ UITools.readUI(this.settingsComponent, AppSettingsState.getInstance());
+ }
}
@Override
public void reset() {
- settingsComponent.setProperties(AppSettingsState.getInstance());
+ if (settingsComponent != null) {
+ UITools.writeUI(settingsComponent, AppSettingsState.getInstance());
+ }
}
@Override
diff --git a/src/main/java/com/github/simiacryptus/aicoder/config/AppSettingsState.java b/src/main/java/com/github/simiacryptus/aicoder/config/AppSettingsState.java
index 2df7891b..f5c7e4fe 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/config/AppSettingsState.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/config/AppSettingsState.java
@@ -32,14 +32,15 @@ public class AppSettingsState implements PersistentStateComponent mostUsedHistory = new HashMap<>();
- private @NotNull List mostRecentHistory = new ArrayList<>();
+ private final @NotNull Map mostUsedHistory = new HashMap<>();
+ private final @NotNull List mostRecentHistory = new ArrayList<>();
public int historyLimit = 10;
public @NotNull String humanLanguage = "English";
public int maxPrompt = 5000;
- public TranslationRequestTemplate translationRequestTemplate = TranslationRequestTemplate.XML;
- public LogLevel apiLogLevel = LogLevel.Debug;
+ public @NotNull TranslationRequestTemplate translationRequestTemplate = TranslationRequestTemplate.XML;
+ public @NotNull LogLevel apiLogLevel = LogLevel.Debug;
public boolean devActions = false;
public AppSettingsState() {
@@ -53,7 +54,7 @@ public TranslationRequest createTranslationRequest() {
return translationRequestTemplate.get(this);
}
- public CompletionRequest createCompletionRequest() {
+ public @NotNull CompletionRequest createCompletionRequest() {
return new CompletionRequest(
"",
temperature,
@@ -97,7 +98,7 @@ public int hashCode() {
return Objects.hash(apiBase, apiKey, model, maxTokens, temperature, translationRequestTemplate, apiLogLevel, devActions, style);
}
- public void addInstructionToHistory(CharSequence instruction) {
+ public void addInstructionToHistory(@NotNull CharSequence instruction) {
synchronized (mostRecentHistory) {
mostRecentHistory.add(instruction.toString());
while(mostRecentHistory.size() > historyLimit) {
diff --git a/src/main/java/com/github/simiacryptus/aicoder/config/SimpleSettingsComponent.java b/src/main/java/com/github/simiacryptus/aicoder/config/SimpleSettingsComponent.java
deleted file mode 100644
index fd3f283b..00000000
--- a/src/main/java/com/github/simiacryptus/aicoder/config/SimpleSettingsComponent.java
+++ /dev/null
@@ -1,165 +0,0 @@
-package com.github.simiacryptus.aicoder.config;
-
-import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.ui.ComboBox;
-import com.intellij.ui.components.JBLabel;
-import com.intellij.util.ui.FormBuilder;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import javax.swing.*;
-import javax.swing.text.JTextComponent;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-
-public class SimpleSettingsComponent {
- private static final Logger log = Logger.getInstance(SimpleSettingsComponent.class);
- protected final boolean verbose = false;
- private volatile @Nullable JPanel mainPanel = null;
-
- /**
- * Builds the main panel for the form.
- *
- * @return the main panel for the form
- */
- private JPanel buildMainPanel() {
- FormBuilder formBuilder = FormBuilder.createFormBuilder();
- for (Field field : this.getClass().getDeclaredFields()) {
- if(Modifier.isStatic(field.getModifiers())) continue;
- try {
- field.setAccessible(true);
- Name nameAnnotation = field.getDeclaredAnnotation(Name.class);
- JComponent component = (JComponent) field.get(this);
- if (null == component) continue;
- if (nameAnnotation != null) {
- formBuilder.addLabeledComponent(new JBLabel(nameAnnotation.value() + ": "), component, 1, false);
- } else {
- formBuilder.addComponentToRightColumn(component, 1);
- }
- } catch (IllegalAccessException e) {
- throw new RuntimeException(e);
- } catch (Throwable e) {
- log.warn("Error processing " + field.getName(), e);
- }
- }
- return formBuilder.addComponentFillVertically(new JPanel(), 0).getPanel();
- }
-
- public void getProperties(@NotNull T settings) {
- for (Field settingsField : settings.getClass().getDeclaredFields()) {
- settingsField.setAccessible(true);
- String settingsFieldName = settingsField.getName();
- try {
- Object newSettingsValue = null;
- Field uiField = this.getClass().getDeclaredField(settingsFieldName);
- Object uiFieldVal = uiField.get(this);
- switch (settingsField.getType().getName()) {
- case "java.lang.String":
- if (uiFieldVal instanceof JTextComponent) {
- newSettingsValue = ((JTextComponent) uiFieldVal).getText();
- } else if (uiFieldVal instanceof ComboBox) {
- newSettingsValue = ((ComboBox) uiFieldVal).getItem();
- }
- break;
- case "int":
- if (uiFieldVal instanceof JTextComponent) {
- newSettingsValue = Integer.parseInt(((JTextComponent) uiFieldVal).getText());
- }
- break;
- case "long":
- if (uiFieldVal instanceof JTextComponent) {
- newSettingsValue = Long.parseLong(((JTextComponent) uiFieldVal).getText());
- }
- break;
- case "double":
- if (uiFieldVal instanceof JTextComponent) {
- newSettingsValue = Double.parseDouble(((JTextComponent) uiFieldVal).getText());
- }
- break;
- case "boolean":
- if (uiFieldVal instanceof JCheckBox) {
- newSettingsValue = ((JCheckBox) uiFieldVal).isSelected();
- } else if (uiFieldVal instanceof JTextComponent) {
- newSettingsValue = Boolean.parseBoolean(((JTextComponent) uiFieldVal).getText());
- }
- break;
- default:
-
- if (java.lang.Enum.class.isAssignableFrom(settingsField.getType())) {
- if (uiFieldVal instanceof ComboBox) {
- ComboBox comboBox = (ComboBox) uiFieldVal;
- CharSequence item = comboBox.getItem();
- newSettingsValue = Enum.valueOf((Class extends Enum>) settingsField.getType(), item.toString());
- }
- }
- break;
- }
- settingsField.set(settings, newSettingsValue);
- } catch (Throwable e) {
- if (verbose) new RuntimeException("Error processing " + settingsField, e).printStackTrace();
- }
- }
- }
-
- public void setProperties(@NotNull T settings) {
- for (Field settingsField : settings.getClass().getDeclaredFields()) {
- settingsField.setAccessible(true);
- String fieldName = settingsField.getName();
- try {
- Field uiField = this.getClass().getDeclaredField(fieldName);
- Object settingsVal = settingsField.get(settings);
- if(null == settingsVal) continue;
- Object uiVal = uiField.get(this);
- switch (settingsField.getType().getName()) {
- case "java.lang.String":
- if (uiVal instanceof JTextComponent) {
- ((JTextComponent) uiVal).setText((String) settingsVal);
- } else if (uiVal instanceof ComboBox) {
- ((ComboBox) uiVal).setItem(settingsVal.toString());
- }
- break;
- case "int":
- if (uiVal instanceof JTextComponent) {
- ((JTextComponent) uiVal).setText(Integer.toString((Integer) settingsVal));
- }
- break;
- case "long":
- if (uiVal instanceof JTextComponent) {
- ((JTextComponent) uiVal).setText(Long.toString((Integer) settingsVal));
- }
- break;
- case "double":
- if (uiVal instanceof JTextComponent) {
- ((JTextComponent) uiVal).setText(Double.toString(((Double) settingsVal)));
- }
- break;
- case "boolean":
- if (uiVal instanceof JCheckBox) {
- ((JCheckBox) uiVal).setSelected(((Boolean) settingsVal));
- } else if (uiVal instanceof JTextComponent) {
- ((JTextComponent) uiVal).setText(Boolean.toString((Boolean) settingsVal));
- }
- break;
- default:
- if (uiVal instanceof ComboBox) {
- ((ComboBox) uiVal).setItem(settingsVal.toString());
- }
- break;
- }
- } catch (Throwable e) {
- if (verbose) new RuntimeException("Error processing " + settingsField, e).printStackTrace();
- }
- }
- }
-
- public JPanel getPanel() {
- if (null == mainPanel) {
- synchronized (this) {
- if (null == mainPanel) {
- mainPanel = buildMainPanel();
- }
- }
- }
- return mainPanel;
- }
-}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/openai/ApiError.java b/src/main/java/com/github/simiacryptus/aicoder/openai/ApiError.java
index 637ec47a..2d922498 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/openai/ApiError.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/openai/ApiError.java
@@ -1,14 +1,20 @@
package com.github.simiacryptus.aicoder.openai;
public class ApiError {
+ @SuppressWarnings("unused")
public String message;
+ @SuppressWarnings("unused")
public String type;
+ @SuppressWarnings("unused")
public String param;
+ @SuppressWarnings("unused")
public Double code;
+ @SuppressWarnings("unused")
public ApiError() {
}
+ @SuppressWarnings("unused")
public ApiError(String message, String type, String param, Double code) {
this.message = message;
this.type = type;
diff --git a/src/main/java/com/github/simiacryptus/aicoder/openai/Choice.java b/src/main/java/com/github/simiacryptus/aicoder/openai/Choice.java
index dbf21965..bdf7d9c9 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/openai/Choice.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/openai/Choice.java
@@ -2,13 +2,18 @@
public class Choice {
public String text;
+ @SuppressWarnings("unused")
public int index;
+ @SuppressWarnings("unused")
public LogProbs logprobs;
+ @SuppressWarnings("unused")
public String finish_reason;
+ @SuppressWarnings("unused")
public Choice() {
}
+ @SuppressWarnings("unused")
public Choice(String text, int index, LogProbs logprobs, String finish_reason) {
this.text = text;
this.index = index;
diff --git a/src/main/java/com/github/simiacryptus/aicoder/openai/CompletionRequest.java b/src/main/java/com/github/simiacryptus/aicoder/openai/CompletionRequest.java
index 4ee31536..c0f62cd8 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/openai/CompletionRequest.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/openai/CompletionRequest.java
@@ -1,30 +1,55 @@
package com.github.simiacryptus.aicoder.openai;
-import com.github.simiacryptus.aicoder.util.IndentedText;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.intellij.openapi.project.Project;
+import com.github.simiacryptus.aicoder.config.AppSettingsState;
+import com.github.simiacryptus.aicoder.util.UITools;
+import com.intellij.util.ui.FormBuilder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import javax.swing.*;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Objects;
-
-import static com.github.simiacryptus.aicoder.util.StringTools.stripPrefix;
-import static com.github.simiacryptus.aicoder.util.StringTools.stripUnbalancedTerminators;
/**
* The CompletionRequest class is used to create a request for completion of a given prompt.
*/
public class CompletionRequest {
+
+ public static class CompletionRequestWithModel extends CompletionRequest {
+
+ public String model;
+
+ public CompletionRequestWithModel(String prompt, double temperature, int max_tokens, Integer logprobs, boolean echo, String model, CharSequence... stop) {
+ super(prompt, temperature, max_tokens, logprobs, echo, stop);
+ this.model = model;
+ }
+
+ public CompletionRequestWithModel(CompletionRequestWithModel other) {
+ super(other);
+ this.model = other.model;
+ }
+
+ public CompletionRequestWithModel(CompletionRequest other, String model) {
+ super(other);
+ this.model = model;
+ }
+ }
public String prompt;
- public String suffix = null;
+ public @Nullable String suffix = null;
+ @SuppressWarnings("unused")
public double temperature;
+ @SuppressWarnings("unused")
public int max_tokens;
- public CharSequence[] stop;
+ public CharSequence @Nullable [] stop;
+ @SuppressWarnings("unused")
public Integer logprobs;
+ @SuppressWarnings("unused")
public boolean echo;
+ @SuppressWarnings("unused")
+ public CompletionRequest() {
+ }
+
public CompletionRequest(String prompt, double temperature, int max_tokens, Integer logprobs, boolean echo, CharSequence... stop) {
this.prompt = prompt;
this.temperature = temperature;
@@ -33,46 +58,61 @@ public CompletionRequest(String prompt, double temperature, int max_tokens, Inte
this.logprobs = logprobs;
this.echo = echo;
}
-
- @NotNull
- public ListenableFuture complete(@Nullable Project project, CharSequence indent) {
- return OpenAI_API.map(OpenAI_API.INSTANCE.complete(project, this), response -> response
- .getFirstChoice()
- .map(Objects::toString)
- .map(String::trim)
- .map(completion -> stripPrefix(completion, this.prompt.trim()))
- .map(String::trim)
- .map(completion -> stripUnbalancedTerminators(completion))
- .map(IndentedText::fromString)
- .map(indentedText -> indentedText.withIndent(indent))
- .map(IndentedText::toString)
- .map(indentedText -> indent + indentedText)
- .orElse(""));
+ public CompletionRequest(CompletionRequest other) {
+ this.prompt = other.prompt;
+ this.temperature = other.temperature;
+ this.max_tokens = other.max_tokens;
+ this.stop = other.stop;
+ this.logprobs = other.logprobs;
+ this.echo = other.echo;
}
+
public @NotNull CompletionRequest appendPrompt(CharSequence prompt) {
this.prompt = this.prompt + prompt;
return this;
}
- public @NotNull CompletionRequest addStops(@NotNull CharSequence... newStops) {
+ public @NotNull CompletionRequest addStops(@NotNull CharSequence @NotNull ... newStops) {
ArrayList stops = new ArrayList<>();
for (CharSequence x : newStops) {
- if (x != null) {
- if (x.length() > 0) {
- stops.add(x);
- }
+ if (x.length() > 0) {
+ stops.add(x);
}
}
if (!stops.isEmpty()) {
- if(null != this.stop) Arrays.stream(this.stop).forEach(stops::add);
+ if (null != this.stop) Arrays.stream(this.stop).forEach(stops::add);
this.stop = stops.stream().distinct().toArray(CharSequence[]::new);
}
return this;
}
- public CompletionRequest setSuffix(CharSequence suffix) {
- this.suffix = suffix.toString();
+ public @NotNull CompletionRequest setSuffix(@Nullable CharSequence suffix) {
+ this.suffix = null==suffix?null:suffix.toString();
return this;
}
+
+ public @NotNull CompletionRequestWithModel showModelEditDialog() {
+ FormBuilder formBuilder = FormBuilder.createFormBuilder();
+ CompletionRequestWithModel withModel = new CompletionRequestWithModel(this, AppSettingsState.getInstance().model);
+ InteractiveRequest ui = new InteractiveRequest(withModel);
+ UITools.addFields(ui, formBuilder);
+ JPanel mainPanel = formBuilder.addComponentFillVertically(new JPanel(), 0).getPanel();
+ UITools.writeUI(ui, withModel);
+ Object[] options = {"OK"};
+ if (JOptionPane.showOptionDialog(
+ null,
+ mainPanel,
+ "OpenAI Completion Request",
+ JOptionPane.NO_OPTION,
+ JOptionPane.PLAIN_MESSAGE,
+ null,
+ options,
+ options[0]) == JOptionPane.OK_OPTION) {
+ UITools.readUI(ui, withModel);
+ return withModel;
+ } else {
+ return withModel;
+ }
+ }
}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/openai/CompletionResponse.java b/src/main/java/com/github/simiacryptus/aicoder/openai/CompletionResponse.java
index 449ebfee..71ae6452 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/openai/CompletionResponse.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/openai/CompletionResponse.java
@@ -6,13 +6,19 @@
import java.util.Optional;
public class CompletionResponse {
+ @SuppressWarnings("unused")
public String id;
+ @SuppressWarnings("unused")
public String object;
+ @SuppressWarnings("unused")
public int created;
+ @SuppressWarnings("unused")
public String model;
public Choice[] choices;
+ @SuppressWarnings("unused")
public ApiError error;
+ @SuppressWarnings("unused")
public Usage usage;
public CompletionResponse() {
diff --git a/src/main/java/com/github/simiacryptus/aicoder/openai/Engine.java b/src/main/java/com/github/simiacryptus/aicoder/openai/Engine.java
index fe3b49b0..b68260ed 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/openai/Engine.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/openai/Engine.java
@@ -1,18 +1,28 @@
package com.github.simiacryptus.aicoder.openai;
public class Engine {
+ @SuppressWarnings("unused")
public String id;
+ @SuppressWarnings("unused")
public boolean ready;
+ @SuppressWarnings("unused")
public String owner;
+ @SuppressWarnings("unused")
public String object;
+ @SuppressWarnings("unused")
public Integer created;
+ @SuppressWarnings("unused")
public String permissions;
+ @SuppressWarnings("unused")
public Integer replicas;
+ @SuppressWarnings("unused")
public Integer max_replicas;
+ @SuppressWarnings("unused")
public Engine() {
}
+ @SuppressWarnings("unused")
public Engine(String id, boolean ready, String owner, String object, Integer created, String permissions, Integer replicas, Integer max_replicas) {
this.id = id;
this.ready = ready;
diff --git a/src/main/java/com/github/simiacryptus/aicoder/openai/InteractiveRequest.java b/src/main/java/com/github/simiacryptus/aicoder/openai/InteractiveRequest.java
new file mode 100644
index 00000000..d1a29e6a
--- /dev/null
+++ b/src/main/java/com/github/simiacryptus/aicoder/openai/InteractiveRequest.java
@@ -0,0 +1,65 @@
+package com.github.simiacryptus.aicoder.openai;
+
+import com.github.simiacryptus.aicoder.config.AppSettingsState;
+import com.github.simiacryptus.aicoder.config.Name;
+import com.github.simiacryptus.aicoder.util.UITools;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.intellij.ui.components.JBTextArea;
+import com.intellij.ui.components.JBTextField;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+import java.util.Arrays;
+
+public class InteractiveRequest {
+ @SuppressWarnings("unused")
+ @Name("Prompt")
+ public final JBTextArea prompt = new JBTextArea(10, 40);
+ @SuppressWarnings("unused")
+ @Name("Suffix")
+ public final JBTextArea suffix = new JBTextArea(2, 40);
+ @SuppressWarnings("unused")
+ @Name("Model")
+ public final JComponent model = OpenAI_API.INSTANCE.getModelSelector();
+ @SuppressWarnings("unused")
+ @Name("Temperature")
+ public final JBTextField temperature = new JBTextField(8);
+ @SuppressWarnings("unused")
+ @Name("Max Tokens")
+ public final JBTextField max_tokens = new JBTextField(8);
+ public final @NotNull JButton testRequest;
+
+ public InteractiveRequest(@NotNull CompletionRequest parent) {
+ testRequest = new JButton(new AbstractAction("Test Request") {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ CompletionRequest.CompletionRequestWithModel withModel = new CompletionRequest.CompletionRequestWithModel(parent, AppSettingsState.getInstance().model);
+ UITools.readUI(InteractiveRequest.this, withModel);
+ ListenableFuture future = OpenAI_API.INSTANCE.complete(null, withModel, "");
+ testRequest.setEnabled(false);
+ Futures.addCallback(future, new FutureCallback<>() {
+ @Override
+ public void onSuccess(@NotNull CharSequence result) {
+ testRequest.setEnabled(true);
+ String text = result.toString();
+ int rows = Math.min(50, text.split("\n").length);
+ int columns = Math.min(200, Arrays.stream(text.split("\n")).mapToInt(String::length).max().getAsInt());
+ JBTextArea area = new JBTextArea(rows, columns);
+ area.setText(text);
+ area.setEditable(false);
+ JOptionPane.showMessageDialog(null, area, "Test Output", JOptionPane.PLAIN_MESSAGE);
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ testRequest.setEnabled(true);
+ UITools.handle(t);
+ }
+ }, OpenAI_API.INSTANCE.pool);
+ }
+ });
+ }
+}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/openai/LogProbs.java b/src/main/java/com/github/simiacryptus/aicoder/openai/LogProbs.java
index 4be2e717..b49c61fa 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/openai/LogProbs.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/openai/LogProbs.java
@@ -3,14 +3,20 @@
import com.fasterxml.jackson.databind.node.ObjectNode;
public class LogProbs {
+ @SuppressWarnings("unused")
public CharSequence[] tokens;
+ @SuppressWarnings("unused")
public double[] token_logprobs;
+ @SuppressWarnings("unused")
public ObjectNode[] top_logprobs;
+ @SuppressWarnings("unused")
public int[] text_offset;
+ @SuppressWarnings("unused")
public LogProbs() {
}
+ @SuppressWarnings("unused")
public LogProbs(CharSequence[] tokens, double[] token_logprobs, ObjectNode[] top_logprobs, int[] text_offset) {
this.tokens = tokens;
this.token_logprobs = token_logprobs;
diff --git a/src/main/java/com/github/simiacryptus/aicoder/openai/OpenAI_API.java b/src/main/java/com/github/simiacryptus/aicoder/openai/OpenAI_API.java
index ea82e63f..e2d551cf 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/openai/OpenAI_API.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/openai/OpenAI_API.java
@@ -1,11 +1,14 @@
package com.github.simiacryptus.aicoder.openai;
import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.github.simiacryptus.aicoder.config.AppSettingsState;
+import com.github.simiacryptus.aicoder.util.IndentedText;
+import com.github.simiacryptus.aicoder.util.StringTools;
import com.github.simiacryptus.aicoder.util.UITools;
import com.google.common.base.Function;
import com.google.common.util.concurrent.*;
@@ -17,6 +20,8 @@
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.progress.util.AbstractProgressIndicatorBase;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.ComboBox;
+import com.intellij.ui.components.JBTextField;
import com.jetbrains.rd.util.LogLevel;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
@@ -29,8 +34,11 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import javax.swing.*;
import java.io.IOException;
+import java.util.Arrays;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
@@ -41,17 +49,60 @@ public final class OpenAI_API {
private static final Logger log = Logger.getInstance(OpenAI_API.class);
public static final OpenAI_API INSTANCE = new OpenAI_API();
- public final ListeningExecutorService pool;
- private transient AppSettingsState settings = null;
+ public final @NotNull ListeningExecutorService pool;
+ private transient @Nullable AppSettingsState settings = null;
- protected AppSettingsState getSettingsState() {
+ private transient ComboBox comboBox = null;
+
+ @NotNull
+ public JComponent getModelSelector() {
+ if (null != comboBox) return comboBox;
+ AppSettingsState settings = AppSettingsState.getInstance();
+ CharSequence apiKey = settings.apiKey;
+ if (apiKey.toString().trim().length() > 0) {
+ try {
+ comboBox = new ComboBox<>(new CharSequence[]{settings.model});
+ onSuccess(INSTANCE.getEngines(), engines -> {
+ JsonNode data = engines.get("data");
+ CharSequence[] items = new CharSequence[data.size()];
+ for (int i = 0; i < data.size(); i++) {
+ items[i] = data.get(i).get("id").asText();
+ }
+ Arrays.sort(items);
+ Arrays.stream(items).forEach(comboBox::addItem);
+ });
+ return comboBox;
+ } catch (Throwable e) {
+ log.warn(e);
+ }
+ }
+ return new JBTextField();
+ }
+
+ @NotNull
+ public ListenableFuture complete(@Nullable Project project, @NotNull CompletionRequest request, CharSequence indent) {
+ return map(complete(project, request), response -> response
+ .getFirstChoice()
+ .map(Objects::toString)
+ .map(String::trim)
+ .map(completion -> stripPrefix(completion, request.prompt.trim()))
+ .map(String::trim)
+ .map(StringTools::stripUnbalancedTerminators)
+ .map(IndentedText::fromString)
+ .map(indentedText -> indentedText.withIndent(indent))
+ .map(IndentedText::toString)
+ .map(indentedText -> indent + indentedText)
+ .orElse(""));
+ }
+
+ private AppSettingsState getSettingsState() {
if (null == this.settings) {
this.settings = AppSettingsState.getInstance();
}
return settings;
}
- protected OpenAI_API() {
+ private OpenAI_API() {
this.pool = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
}
@@ -60,32 +111,56 @@ public ListenableFuture getEngines() {
return pool.submit(() -> getMapper().readValue(get(getSettingsState().apiBase + "/engines"), ObjectNode.class));
}
- protected String post(String url, @NotNull String body) throws IOException, InterruptedException {
+ private String post(String url, @NotNull String body) throws IOException, InterruptedException {
return post(url, body, 3);
}
- public ListenableFuture complete(@Nullable Project project, @NotNull CompletionRequest completionRequest) {
+ private @NotNull ListenableFuture complete(@Nullable Project project, @NotNull CompletionRequest completionRequest) {
AppSettingsState settings = getSettingsState();
- if (null != completionRequest.suffix) {
- if (completionRequest.suffix.trim().length() == 0) {
- completionRequest.setSuffix(null);
+ CompletionRequest.CompletionRequestWithModel withModel;
+ if (!(completionRequest instanceof CompletionRequest.CompletionRequestWithModel)) {
+ if (!AppSettingsState.getInstance().devActions) {
+ withModel = new CompletionRequest.CompletionRequestWithModel(completionRequest, AppSettingsState.getInstance().model);
+ } else {
+ withModel = completionRequest.showModelEditDialog();
+ }
+ } else {
+ withModel = (CompletionRequest.CompletionRequestWithModel) completionRequest;
+ }
+
+ if (null != withModel.suffix) {
+ if (withModel.suffix.trim().length() == 0) {
+ withModel.setSuffix(null);
} else {
- completionRequest.echo = false;
+ withModel.echo = false;
}
}
- if (null != completionRequest.stop && completionRequest.stop.length == 0) {
- completionRequest.stop = null;
+ if (null != withModel.stop && withModel.stop.length == 0) {
+ withModel.stop = null;
}
- if (completionRequest.prompt.length() > settings.maxPrompt)
- throw new IllegalArgumentException("Prompt too long:" + completionRequest.prompt.length() + " chars");
+ if (withModel.prompt.length() > settings.maxPrompt)
+ throw new IllegalArgumentException("Prompt too long:" + withModel.prompt.length() + " chars");
+ return complete(project, new CompletionRequest(withModel), settings, withModel.model);
+ }
+
+ @NotNull
+ private ListenableFuture complete(@Nullable Project project, @NotNull CompletionRequest completionRequest, @NotNull AppSettingsState settings, @NotNull final String model) {
return OpenAI_API.map(moderateAsync(project, completionRequest.prompt), x -> {
try {
Task.WithResult task = new Task.WithResult<>(project, "OpenAI Text Completion", false) {
@Override
- protected CompletionResponse compute(@NotNull ProgressIndicator indicator) throws Exception {
+ protected @NotNull CompletionResponse compute(@NotNull ProgressIndicator indicator) throws Exception {
try {
+ if (completionRequest.suffix == null) {
+ log(settings.apiLogLevel, String.format("Text Completion Request\nPrefix:\n\t%s\n",
+ completionRequest.prompt.replace("\n", "\n\t")));
+ } else {
+ log(settings.apiLogLevel, String.format("Text Completion Request\nPrefix:\n\t%s\nSuffix:\n\t%s\n",
+ completionRequest.prompt.replace("\n", "\n\t"),
+ completionRequest.suffix.replace("\n", "\n\t")));
+ }
String request = getMapper().writeValueAsString(completionRequest);
- String result = post(settings.apiBase + "/engines/" + settings.model + "/completions", request);
+ String result = post(settings.apiBase + "/engines/" + model + "/completions", request);
JsonObject jsonObject = new Gson().fromJson(result, JsonObject.class);
if (jsonObject.has("error")) {
JsonObject errorObject = jsonObject.getAsJsonObject("error");
@@ -99,13 +174,10 @@ protected CompletionResponse compute(@NotNull ProgressIndicator indicator) throw
}
String completionResult = stripPrefix(completionResponse.getFirstChoice().orElse("").toString().trim(), completionRequest.prompt.trim());
if (completionRequest.suffix == null) {
- log(settings.apiLogLevel, String.format("Text Completion Request\nPrefix:\n\t%s\nCompletion:\n\t%s",
- completionRequest.prompt.replace("\n", "\n\t"),
+ log(settings.apiLogLevel, String.format("Text Completion Completion:\n\t%s",
completionResult.replace("\n", "\n\t")));
} else {
- log(settings.apiLogLevel, String.format("Text Completion Request\nPrefix:\n\t%s\nSuffix:\n\t%s\nCompletion:\n\t%s",
- completionRequest.prompt.replace("\n", "\n\t"),
- completionRequest.suffix.replace("\n", "\n\t"),
+ log(settings.apiLogLevel, String.format("Text Completion Completion:\n\t%s",
completionResult.replace("\n", "\n\t")));
}
return completionResponse;
@@ -129,12 +201,12 @@ protected CompletionResponse compute(@NotNull ProgressIndicator indicator) throw
}
public static
- ListenableFuture map(ListenableFuture moderateAsync, Function super I, ? extends O> o) {
+ @NotNull ListenableFuture map(@NotNull ListenableFuture moderateAsync, @NotNull Function super I, ? extends O> o) {
return Futures.transform(moderateAsync, o, INSTANCE.pool);
}
- public static void onSuccess(ListenableFuture moderateAsync, Consumer super I> o) {
- Futures.addCallback(moderateAsync, new FutureCallback() {
+ public static void onSuccess(@NotNull ListenableFuture moderateAsync, @NotNull Consumer super I> o) {
+ Futures.addCallback(moderateAsync, new FutureCallback<>() {
@Override
public void onSuccess(I result) {
o.accept(result);
@@ -147,7 +219,7 @@ public void onFailure(Throwable t) {
}, INSTANCE.pool);
}
- private void log(LogLevel level, String msg) {
+ private void log(@NotNull LogLevel level, @NotNull String msg) {
String message = msg.trim().replace("\n", "\n\t");
switch (level) {
case Error:
@@ -167,9 +239,9 @@ private void log(LogLevel level, String msg) {
@NotNull
private ListenableFuture> moderateAsync(@Nullable Project project, @NotNull String text) {
- Task.WithResult, Exception> task = new Task.WithResult, Exception>(project, "OpenAI Moderation", false) {
+ Task.WithResult, Exception> task = new Task.WithResult<>(project, "OpenAI Moderation", false) {
@Override
- protected ListenableFuture> compute(@NotNull ProgressIndicator indicator) throws Exception {
+ protected @NotNull ListenableFuture> compute(@NotNull ProgressIndicator indicator) throws Exception {
return pool.submit(() -> {
String body = null;
try {
@@ -222,7 +294,7 @@ protected ListenableFuture> compute(@NotNull ProgressIndicator indicator) thro
* @throws IOException If an IOException is thrown and the number of retries is exceeded.
* @throws InterruptedException If the thread is interrupted while sleeping.
*/
- protected String post(String url, @NotNull String json, int retries) throws IOException, InterruptedException {
+ private String post(String url, @NotNull String json, int retries) throws IOException, InterruptedException {
try {
HttpClientBuilder client = HttpClientBuilder.create();
HttpPost request = new HttpPost(url);
@@ -243,7 +315,8 @@ protected String post(String url, @NotNull String json, int retries) throws IOEx
}
}
- protected @NotNull ObjectMapper getMapper() {
+ @NotNull
+ private ObjectMapper getMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper
.enable(SerializationFeature.INDENT_OUTPUT)
@@ -254,15 +327,15 @@ protected String post(String url, @NotNull String json, int retries) throws IOEx
return mapper;
}
- protected void authorize(@NotNull HttpRequestBase request) throws IOException {
+ private void authorize(@NotNull HttpRequestBase request) throws IOException {
AppSettingsState settingsState = getSettingsState();
String apiKey = settingsState.apiKey;
- if (apiKey == null || apiKey.length() == 0) {
+ if (apiKey.length() == 0) {
synchronized (settingsState) {
apiKey = settingsState.apiKey;
- if (apiKey == null || apiKey.length() == 0) {
+ if (apiKey.length() == 0) {
apiKey = UITools.queryAPIKey();
- settingsState.apiKey = apiKey;
+ settingsState.apiKey = Objects.requireNonNull(apiKey);
}
}
}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/openai/Response.java b/src/main/java/com/github/simiacryptus/aicoder/openai/Response.java
index c316f194..96862660 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/openai/Response.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/openai/Response.java
@@ -1,12 +1,16 @@
package com.github.simiacryptus.aicoder.openai;
public class Response {
+ @SuppressWarnings("unused")
public String object;
+ @SuppressWarnings("unused")
private Engine[] data;
+ @SuppressWarnings("unused")
public Response() {
}
+ @SuppressWarnings("unused")
public Response(String object, Engine[] data) {
this.object = object;
this.data = data;
diff --git a/src/main/java/com/github/simiacryptus/aicoder/openai/Usage.java b/src/main/java/com/github/simiacryptus/aicoder/openai/Usage.java
index 13994645..ae08ccbf 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/openai/Usage.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/openai/Usage.java
@@ -1,10 +1,14 @@
package com.github.simiacryptus.aicoder.openai;
public class Usage {
+ @SuppressWarnings("unused")
public int prompt_tokens;
+ @SuppressWarnings("unused")
public int completion_tokens;
+ @SuppressWarnings("unused")
public int total_tokens;
+ @SuppressWarnings("unused")
public Usage() {
}
}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/openai/translate/BaseTranslationRequest.java b/src/main/java/com/github/simiacryptus/aicoder/openai/translate/BaseTranslationRequest.java
index ae52505d..bb8e6b28 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/openai/translate/BaseTranslationRequest.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/openai/translate/BaseTranslationRequest.java
@@ -1,6 +1,7 @@
package com.github.simiacryptus.aicoder.openai.translate;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.HashMap;
@@ -12,25 +13,25 @@ public abstract class BaseTranslationRequest
private CharSequence outputTag;
private CharSequence instruction;
@NotNull
- private Map inputAttr = new HashMap<>();
+ private final Map inputAttr = new HashMap<>();
@NotNull
- private Map outputAttr = new HashMap<>();
+ private final Map outputAttr = new HashMap<>();
private CharSequence originalText;
private double temperature;
private int maxTokens;
@Override
- public String getInputTag() {
+ public @NotNull String getInputTag() {
return inputTag.toString();
}
@Override
- public String getOutputTag() {
+ public @NotNull String getOutputTag() {
return outputTag.toString();
}
@Override
- public CharSequence getInstruction() {
+ public @NotNull CharSequence getInstruction() {
return instruction.toString();
}
@@ -47,7 +48,7 @@ public Map getOutputAttr() {
}
@Override
- public String getOriginalText() {
+ public @NotNull String getOriginalText() {
return originalText.toString();
}
@@ -62,43 +63,43 @@ public int getMaxTokens() {
}
@Override
- public T setInputType(CharSequence inputTag) {
+ public @NotNull T setInputType(CharSequence inputTag) {
this.inputTag = inputTag;
return (T) this;
}
@Override
- public T setOutputType(CharSequence outputTag) {
+ public @NotNull T setOutputType(CharSequence outputTag) {
this.outputTag = outputTag;
return (T) this;
}
@Override
- public T setInstruction(CharSequence instruction) {
+ public @NotNull T setInstruction(CharSequence instruction) {
this.instruction = instruction;
return (T) this;
}
@Override
- public T setInputText(CharSequence originalText) {
+ public @NotNull T setInputText(CharSequence originalText) {
this.originalText = originalText;
return (T) this;
}
@Override
- public T setTemperature(double temperature) {
+ public @NotNull T setTemperature(double temperature) {
this.temperature = temperature;
return (T) this;
}
@Override
- public T setMaxTokens(int maxTokens) {
+ public @NotNull T setMaxTokens(int maxTokens) {
this.maxTokens = maxTokens;
return (T) this;
}
@Override
- public T setInputAttribute(CharSequence key, CharSequence value) {
+ public @NotNull T setInputAttribute(CharSequence key, @Nullable CharSequence value) {
if(null == value || value.length()==0) {
inputAttr.remove(key);
} else {
@@ -108,7 +109,7 @@ public T setInputAttribute(CharSequence key, CharSequence value) {
}
@Override
- public T setOutputAttrute(CharSequence key, CharSequence value) {
+ public @NotNull T setOutputAttrute(CharSequence key, @Nullable CharSequence value) {
if(null == value || value.length()==0) {
outputAttr.remove(key);
} else {
diff --git a/src/main/java/com/github/simiacryptus/aicoder/openai/translate/TranslationRequest.java b/src/main/java/com/github/simiacryptus/aicoder/openai/translate/TranslationRequest.java
index c25dc7cc..b476c868 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/openai/translate/TranslationRequest.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/openai/translate/TranslationRequest.java
@@ -8,20 +8,28 @@
public interface TranslationRequest {
@NotNull CompletionRequest buildCompletionRequest();
+ @SuppressWarnings("unused")
String getInputTag();
+ @SuppressWarnings("unused")
String getOutputTag();
+ @SuppressWarnings("unused")
CharSequence getInstruction();
+ @SuppressWarnings("unused")
@NotNull Map getInputAttr();
+ @SuppressWarnings("unused")
@NotNull Map getOutputAttr();
+ @SuppressWarnings("unused")
String getOriginalText();
+ @SuppressWarnings("unused")
double getTemperature();
+ @SuppressWarnings("unused")
int getMaxTokens();
TranslationRequest setInputType(CharSequence inputTag);
@@ -35,7 +43,9 @@ public interface TranslationRequest {
TranslationRequest setInputText(CharSequence originalText);
+ @SuppressWarnings("unused")
TranslationRequest setTemperature(double temperature);
+ @SuppressWarnings("unused")
TranslationRequest setMaxTokens(int maxTokens);
}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/openai/translate/TranslationRequestTemplate.java b/src/main/java/com/github/simiacryptus/aicoder/openai/translate/TranslationRequestTemplate.java
index 8b970984..1f8c25ad 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/openai/translate/TranslationRequestTemplate.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/openai/translate/TranslationRequestTemplate.java
@@ -3,10 +3,9 @@
import com.github.simiacryptus.aicoder.config.AppSettingsState;
import java.util.function.Function;
-import java.util.function.Supplier;
public enum TranslationRequestTemplate {
- XML(config -> new TranslationRequest_XML(config));
+ XML(TranslationRequest_XML::new);
private final Function fn;
TranslationRequestTemplate(Function fn) {
diff --git a/src/main/java/com/github/simiacryptus/aicoder/openai/translate/TranslationRequest_XML.java b/src/main/java/com/github/simiacryptus/aicoder/openai/translate/TranslationRequest_XML.java
index 58334ecc..b10f483e 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/openai/translate/TranslationRequest_XML.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/openai/translate/TranslationRequest_XML.java
@@ -8,7 +8,7 @@
public class TranslationRequest_XML extends BaseTranslationRequest {
- public TranslationRequest_XML(AppSettingsState settings) {
+ public TranslationRequest_XML(@NotNull AppSettingsState settings) {
setTemperature(settings.temperature);
setMaxTokens(settings.maxTokens);
}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/util/BlockComment.java b/src/main/java/com/github/simiacryptus/aicoder/util/BlockComment.java
index 21b0cbce..7c376742 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/util/BlockComment.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/util/BlockComment.java
@@ -18,7 +18,7 @@ public Factory(String blockPrefix, String linePrefix, String blockSuffix) {
}
@Override
- public BlockComment fromString(String text) {
+ public @NotNull BlockComment fromString(String text) {
text = StringTools.stripSuffix(StringTools.trimSuffix(text.replace("\t", TAB_REPLACEMENT)), blockSuffix.trim());
@NotNull CharSequence indent = StringTools.getWhitespacePrefix(text.split(DELIMITER));
return new BlockComment(blockPrefix, linePrefix, blockSuffix, indent, Arrays.stream(text.split(DELIMITER))
@@ -30,7 +30,7 @@ public BlockComment fromString(String text) {
}
@Override
- public boolean looksLike(String text) {
+ public boolean looksLike(@NotNull String text) {
return text.trim().startsWith(blockPrefix) && text.trim().endsWith(blockSuffix);
}
}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/ComputerLanguage.java b/src/main/java/com/github/simiacryptus/aicoder/util/ComputerLanguage.java
similarity index 83%
rename from src/main/java/com/github/simiacryptus/aicoder/ComputerLanguage.java
rename to src/main/java/com/github/simiacryptus/aicoder/util/ComputerLanguage.java
index d0e988b4..ac931a98 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/ComputerLanguage.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/util/ComputerLanguage.java
@@ -1,12 +1,14 @@
-package com.github.simiacryptus.aicoder;
+package com.github.simiacryptus.aicoder.util;
-import com.github.simiacryptus.aicoder.util.BlockComment;
-import com.github.simiacryptus.aicoder.util.LineComment;
-import com.github.simiacryptus.aicoder.util.TextBlockFactory;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
public enum ComputerLanguage {
Java(new Configuration()
@@ -21,6 +23,18 @@ public enum ComputerLanguage {
.setBlockComments(new BlockComment.Factory("/*", "", "*/"))
.setDocComments(new BlockComment.Factory("/**", "*", "*/"))
.setFileExtensions("cpp")),
+ SVG(new Configuration()
+ .setDocumentationStyle("SVG")
+ .setLineComments(new LineComment.Factory(""))
+ .setDocComments(new BlockComment.Factory(""))
+ .setFileExtensions("svg")),
+ OpenSCAD(new Configuration()
+ .setDocumentationStyle("OpenSCAD")
+ .setLineComments(new LineComment.Factory("//"))
+ .setBlockComments(new BlockComment.Factory("/*", "", "*/"))
+ .setDocComments(new BlockComment.Factory("/**", "*", "*/"))
+ .setFileExtensions("scad")),
Bash(new Configuration()
.setLineComments(new LineComment.Factory("#"))
.setFileExtensions("sh")),
@@ -141,7 +155,7 @@ public enum ComputerLanguage {
.setLineComments(new LineComment.Factory("//"))
.setBlockComments(new BlockComment.Factory("/*", "", "*/"))
.setDocComments(new BlockComment.Factory("/**", "*", "*/"))
- .setFileExtensions("kotlin", "kt")),
+ .setFileExtensions("kotlin", "kt", "kts")),
Lisp(new Configuration()
.setLineComments(new LineComment.Factory(";"))
.setBlockComments(new BlockComment.Factory("/*", "", "*/"))
@@ -265,13 +279,13 @@ public enum ComputerLanguage {
.setLineComments(new LineComment.Factory("#"))
.setFileExtensions("zsh"));
- public final List extensions;
+ public final @NotNull List extensions;
public final String docStyle;
- public final TextBlockFactory> lineComment;
- public final TextBlockFactory> blockComment;
- public final TextBlockFactory> docComment;
+ public final @Nullable TextBlockFactory> lineComment;
+ public final @Nullable TextBlockFactory> blockComment;
+ public final @Nullable TextBlockFactory> docComment;
- ComputerLanguage(Configuration configuration) {
+ ComputerLanguage(@NotNull Configuration configuration) {
this.extensions = Arrays.asList(configuration.getFileExtensions());
this.docStyle = configuration.getDocumentationStyle();
this.lineComment = configuration.getLineComments();
@@ -284,31 +298,39 @@ public static ComputerLanguage findByExtension(CharSequence extension) {
return Arrays.stream(values()).filter(x -> x.extensions.contains(extension)).findAny().orElse(null);
}
- public CharSequence getMultilineCommentSuffix() {
+ @Nullable
+ public static ComputerLanguage getComputerLanguage(@NotNull AnActionEvent e) {
+ VirtualFile file = e.getData(CommonDataKeys.VIRTUAL_FILE);
+ if (file == null) return null;
+ String extension = file.getExtension() != null ? file.getExtension().toLowerCase() : "";
+ return findByExtension(extension);
+ }
+
+ public @Nullable CharSequence getMultilineCommentSuffix() {
if (docComment instanceof BlockComment.Factory) {
return ((BlockComment.Factory) docComment).blockSuffix;
}
return null;
}
- public TextBlockFactory> getCommentModel(String text) {
- if (docComment.looksLike(text)) return docComment;
- if (blockComment.looksLike(text)) return blockComment;
+ public @Nullable TextBlockFactory> getCommentModel(String text) {
+ if (Objects.requireNonNull(docComment).looksLike(text)) return docComment;
+ if (Objects.requireNonNull(blockComment).looksLike(text)) return blockComment;
return lineComment;
}
static class Configuration {
private String documentationStyle = "";
private CharSequence[] fileExtensions = new CharSequence[]{};
- private TextBlockFactory> lineComments = null;
- private TextBlockFactory> blockComments = null;
- private TextBlockFactory> docComments = null;
+ private @Nullable TextBlockFactory> lineComments = null;
+ private @Nullable TextBlockFactory> blockComments = null;
+ private @Nullable TextBlockFactory> docComments = null;
public String getDocumentationStyle() {
return documentationStyle;
}
- public Configuration setDocumentationStyle(String documentationStyle) {
+ public @NotNull Configuration setDocumentationStyle(String documentationStyle) {
this.documentationStyle = documentationStyle;
return this;
}
@@ -317,36 +339,36 @@ public CharSequence[] getFileExtensions() {
return fileExtensions;
}
- public Configuration setFileExtensions(CharSequence... fileExtensions) {
+ public @NotNull Configuration setFileExtensions(CharSequence... fileExtensions) {
this.fileExtensions = fileExtensions;
return this;
}
- public TextBlockFactory> getLineComments() {
+ public @Nullable TextBlockFactory> getLineComments() {
return lineComments;
}
- public Configuration setLineComments(TextBlockFactory> lineComments) {
+ public @NotNull Configuration setLineComments(TextBlockFactory> lineComments) {
this.lineComments = lineComments;
return this;
}
- public TextBlockFactory> getBlockComments() {
+ public @Nullable TextBlockFactory> getBlockComments() {
if (null == blockComments) return getLineComments();
return blockComments;
}
- public Configuration setBlockComments(TextBlockFactory> blockComments) {
+ public @NotNull Configuration setBlockComments(TextBlockFactory> blockComments) {
this.blockComments = blockComments;
return this;
}
- public TextBlockFactory> getDocComments() {
+ public @Nullable TextBlockFactory> getDocComments() {
if (null == docComments) return getBlockComments();
return docComments;
}
- public Configuration setDocComments(TextBlockFactory> docComments) {
+ public @NotNull Configuration setDocComments(TextBlockFactory> docComments) {
this.docComments = docComments;
return this;
}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/util/IndentedText.java b/src/main/java/com/github/simiacryptus/aicoder/util/IndentedText.java
index 64ccbdb1..8927d5d2 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/util/IndentedText.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/util/IndentedText.java
@@ -23,12 +23,14 @@ public CharSequence getIndent() {
return indent;
}
+ @SuppressWarnings("unused")
public static class Factory implements TextBlockFactory {
@Override
- public IndentedText fromString(String text) {
+ public @NotNull IndentedText fromString(String text) {
return IndentedText.fromString(text);
}
+ @SuppressWarnings("unused")
@Override
public boolean looksLike(String text) {
return true;
@@ -36,7 +38,7 @@ public boolean looksLike(String text) {
}
protected CharSequence indent;
- protected CharSequence textBlock[];
+ protected CharSequence[] textBlock;
public IndentedText(CharSequence indent, CharSequence... textBlock) {
this.indent = indent;
diff --git a/src/main/java/com/github/simiacryptus/aicoder/util/LineComment.java b/src/main/java/com/github/simiacryptus/aicoder/util/LineComment.java
index 21098d7c..3347775b 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/util/LineComment.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/util/LineComment.java
@@ -14,7 +14,7 @@ public Factory(String commentPrefix) {
}
@Override
- public LineComment fromString(String text) {
+ public @NotNull LineComment fromString(String text) {
text = text.replace("\t", TAB_REPLACEMENT);
CharSequence indent = StringTools.getWhitespacePrefix(text.split(DELIMITER));
return new LineComment(commentPrefix, indent, Arrays.stream(text.split(DELIMITER))
@@ -25,7 +25,7 @@ public LineComment fromString(String text) {
}
@Override
- public boolean looksLike(String text) {
+ public boolean looksLike(@NotNull String text) {
return Arrays.stream(text.split(DELIMITER)).allMatch(x->x.trim().startsWith(commentPrefix));
}
}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/util/StringTools.java b/src/main/java/com/github/simiacryptus/aicoder/util/StringTools.java
index e0c09a24..5bb8ab00 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/util/StringTools.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/util/StringTools.java
@@ -18,7 +18,7 @@ public class StringTools {
* @return The input string with unbalanced terminators removed.
* @throws IllegalArgumentException If the input string is unbalanced.
*/
- public static CharSequence stripUnbalancedTerminators(CharSequence input) {
+ public static @NotNull CharSequence stripUnbalancedTerminators(@NotNull CharSequence input) {
int openCount = 0;
boolean inQuotes = false;
StringBuilder output = new StringBuilder();
@@ -83,7 +83,7 @@ public static CharSequence stripUnbalancedTerminators(CharSequence input) {
}
}
- public static String lineWrapping(CharSequence description, int width) {
+ public static @NotNull String lineWrapping(@NotNull CharSequence description, int width) {
StringBuilder output = new StringBuilder();
String[] lines = description.toString().split("\n");
int lineLength = 0;
@@ -105,7 +105,7 @@ public static String lineWrapping(CharSequence description, int width) {
return output.toString();
}
- private static String wrapSentence(CharSequence line, int width, AtomicInteger xPointer) {
+ private static @NotNull String wrapSentence(@NotNull CharSequence line, int width, @NotNull AtomicInteger xPointer) {
StringBuilder sentenceBuffer = new StringBuilder();
String[] words = line.toString().split(" ");
for (String word : words) {
@@ -122,7 +122,7 @@ private static String wrapSentence(CharSequence line, int width, AtomicInteger x
return sentenceBuffer.toString();
}
- public static CharSequence toString(int[] ints) {
+ public static @NotNull CharSequence toString(int @NotNull [] ints) {
char[] chars = new char[ints.length];
for (int i = 0; i < ints.length; i++) {
chars[i] = (char) ints[i];
@@ -131,22 +131,22 @@ public static CharSequence toString(int[] ints) {
}
@NotNull
- public static CharSequence getWhitespacePrefix(CharSequence... lines) {
+ public static CharSequence getWhitespacePrefix(CharSequence @NotNull ... lines) {
return Arrays.stream(lines)
- .map(l -> toString(l.chars().takeWhile(i -> Character.isWhitespace(i)).toArray()))
+ .map(l -> toString(l.chars().takeWhile(Character::isWhitespace).toArray()))
.filter(x -> x.length()>0)
- .min(Comparator.comparing(x -> x.length())).orElse("");
+ .min(Comparator.comparing(CharSequence::length)).orElse("");
}
@NotNull
- public static String getWhitespaceSuffix(CharSequence... lines) {
+ public static String getWhitespaceSuffix(CharSequence @NotNull ... lines) {
return reverse(Arrays.stream(lines)
.map(StringTools::reverse)
- .map(l -> toString(l.chars().takeWhile(i -> Character.isWhitespace(i)).toArray()))
- .max(Comparator.comparing(x -> x.length())).orElse("")).toString();
+ .map(l -> toString(l.chars().takeWhile(Character::isWhitespace).toArray()))
+ .max(Comparator.comparing(CharSequence::length)).orElse("")).toString();
}
- private static CharSequence reverse(CharSequence l) {
+ private static @NotNull CharSequence reverse(@NotNull CharSequence l) {
return new StringBuffer(l).reverse().toString();
}
@@ -162,8 +162,8 @@ public static List trim(List items, int max, boolean
return items;
}
- public static String transposeMarkdownTable(String table, boolean inputHeader, boolean outputHeader) {
- String[][] cells = parseMarkdownTable(table, inputHeader);
+ public static @NotNull String transposeMarkdownTable(@NotNull String table, boolean inputHeader, boolean outputHeader) {
+ CharSequence[][] cells = parseMarkdownTable(table, inputHeader);
StringBuilder transposedTable = new StringBuilder();
int columns = cells[0].length;
int rows = cells.length;
@@ -172,19 +172,19 @@ public static String transposeMarkdownTable(String table, boolean inputHeader, b
transposedTable.append("|");
for (int row = 0; row < rows; row++) {
String cellValue;
- String[] rowCells = cells[row];
+ CharSequence[] rowCells = cells[row];
if (outputHeader) {
if (column < 1) {
- cellValue = rowCells[column].trim();
+ cellValue = rowCells[column].toString().trim();
} else if (column == 1) {
cellValue = "---";
} else if ((column - 1) >= rowCells.length) {
cellValue = "";
} else {
- cellValue = rowCells[column - 1].trim();
+ cellValue = rowCells[column - 1].toString().trim();
}
} else {
- cellValue = rowCells[column].trim();
+ cellValue = rowCells[column].toString().trim();
}
transposedTable.append(" ").append(cellValue).append(" |");
}
@@ -193,17 +193,17 @@ public static String transposeMarkdownTable(String table, boolean inputHeader, b
return transposedTable.toString();
}
- private static String[][] parseMarkdownTable(String table, boolean removeHeader) {
- ArrayList rows = new ArrayList(Arrays.stream(table.split("\n")).map(x -> Arrays.stream(x.split("\\|")).filter(cell -> cell.length() > 0).toArray(CharSequence[]::new)).collect(Collectors.toList()));
+ private static CharSequence[] @NotNull [] parseMarkdownTable(@NotNull String table, boolean removeHeader) {
+ ArrayList rows = new ArrayList<>(Arrays.stream(table.split("\n")).map(x -> Arrays.stream(x.split("\\|")).filter(cell -> cell.length() > 0).toArray(CharSequence[]::new)).collect(Collectors.toList()));
if (removeHeader) {
rows.remove(1);
}
return rows.stream()
//.filter(x -> x.length == rows.get(0).length)
- .toArray(String[][]::new);
+ .toArray(CharSequence[][]::new);
}
- public static CharSequence getPrefixForContext(String text) {
+ public static @NotNull CharSequence getPrefixForContext(@NotNull String text) {
return getPrefixForContext(text, 512, ".", "\n", ",", ";");
}
@@ -215,7 +215,7 @@ public static CharSequence getPrefixForContext(String text) {
* @param delimiters The delimiters to split the text by.
* @return The prefix for the given context.
*/
- public static CharSequence getPrefixForContext(String text, int idealLength, CharSequence... delimiters) {
+ public static @NotNull CharSequence getPrefixForContext(@NotNull String text, int idealLength, CharSequence... delimiters) {
List candidates = Stream.of(delimiters).flatMap(d -> {
StringBuilder sb = new StringBuilder();
String[] split = text.split(Pattern.quote(d.toString()));
@@ -233,7 +233,7 @@ public static CharSequence getPrefixForContext(String text, int idealLength, Cha
return winner.get();
}
- public static CharSequence getSuffixForContext(String text) {
+ public static @NotNull CharSequence getSuffixForContext(@NotNull String text) {
return getSuffixForContext(text, 512, ".", "\n", ",", ";");
}
@@ -247,7 +247,7 @@ public static CharSequence getSuffixForContext(String text) {
* @return The suffix for the given context.
*/
@NotNull
- public static CharSequence getSuffixForContext(String text, int idealLength, CharSequence... delimiters) {
+ public static CharSequence getSuffixForContext(@NotNull String text, int idealLength, CharSequence... delimiters) {
List candidates = Stream.of(delimiters).flatMap(d -> {
StringBuilder sb = new StringBuilder();
String[] split = text.split(Pattern.quote(d.toString()));
diff --git a/src/main/java/com/github/simiacryptus/aicoder/util/StyleUtil.java b/src/main/java/com/github/simiacryptus/aicoder/util/StyleUtil.java
index 47a28eb5..fb7e4d8b 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/util/StyleUtil.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/util/StyleUtil.java
@@ -1,11 +1,11 @@
package com.github.simiacryptus.aicoder.util;
-import com.github.simiacryptus.aicoder.ComputerLanguage;
import com.github.simiacryptus.aicoder.config.AppSettingsState;
import com.github.simiacryptus.aicoder.openai.CompletionRequest;
import com.github.simiacryptus.aicoder.openai.OpenAI_API;
import com.google.common.util.concurrent.ListenableFuture;
import com.intellij.openapi.diagnostic.Logger;
+import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.Arrays;
@@ -13,6 +13,7 @@
import java.util.Random;
public class StyleUtil {
+ @SuppressWarnings("unused")
private static final Logger log = Logger.getInstance(StyleUtil.class);
/**
@@ -124,7 +125,7 @@ public static void demoStyle(CharSequence style) {
* @param language The language of the code snippet.
* @param code The code snippet to be described.
*/
- public static void demoStyle(CharSequence style, ComputerLanguage language, String code) {
+ public static void demoStyle(CharSequence style, @NotNull ComputerLanguage language, @NotNull String code) {
OpenAI_API.onSuccess(describeTest(style, language, code), description -> {
CharSequence message = String.format("This code:\n %s\nwas described as:\n %s", code.replace("\n", "\n "), description.toString().replace("\n", "\n "));
JOptionPane.showMessageDialog(null, message, "Style Demo", JOptionPane.INFORMATION_MESSAGE);
@@ -139,7 +140,7 @@ public static void demoStyle(CharSequence style, ComputerLanguage language, Stri
* @param code The code.
* @return A description of the test in the specified style and language.
*/
- public static ListenableFuture describeTest(CharSequence style, ComputerLanguage language, String code) {
+ public static @NotNull ListenableFuture describeTest(CharSequence style, @NotNull ComputerLanguage language, String code) {
AppSettingsState settings = AppSettingsState.getInstance();
CompletionRequest completionRequest = settings.createTranslationRequest()
.setInstruction(String.format("Explain this %s in %s (%s)", language.name(), settings.humanLanguage, style))
@@ -150,7 +151,7 @@ public static ListenableFuture describeTest(CharSequence style, Co
.setOutputAttrute("type", "description")
.setOutputAttrute("style", style)
.buildCompletionRequest();
- ListenableFuture future = completionRequest.complete(null, "");
+ ListenableFuture future = OpenAI_API.INSTANCE.complete(null, completionRequest, "");
return OpenAI_API.map(future, x->StringTools.lineWrapping(x.toString().trim(), 120));
}
}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/util/TextBlock.java b/src/main/java/com/github/simiacryptus/aicoder/util/TextBlock.java
index 7bbe3b0f..950d22ba 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/util/TextBlock.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/util/TextBlock.java
@@ -8,8 +8,8 @@
public interface TextBlock {
- public static final CharSequence TAB_REPLACEMENT = " ";
- public static final String DELIMITER = "\n";
+ CharSequence TAB_REPLACEMENT = " ";
+ String DELIMITER = "\n";
CharSequence[] rawString();
@@ -19,7 +19,7 @@ default String getTextBlock() {
@NotNull TextBlock withIndent(CharSequence indent);
- default Stream stream() {
+ default @NotNull Stream stream() {
return Arrays.stream(rawString());
}
}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/util/TextBlockFactory.java b/src/main/java/com/github/simiacryptus/aicoder/util/TextBlockFactory.java
index 9eb29abf..b9a18ac6 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/util/TextBlockFactory.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/util/TextBlockFactory.java
@@ -1,8 +1,11 @@
package com.github.simiacryptus.aicoder.util;
+import org.jetbrains.annotations.NotNull;
+
public interface TextBlockFactory {
T fromString(String text);
- default CharSequence toString(T text) {
+ @SuppressWarnings("unused")
+ default CharSequence toString(@NotNull T text) {
return text.toString();
}
boolean looksLike(String text);
diff --git a/src/main/java/com/github/simiacryptus/aicoder/util/TextReplacementAction.java b/src/main/java/com/github/simiacryptus/aicoder/util/TextReplacementAction.java
index d3781557..fa414034 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/util/TextReplacementAction.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/util/TextReplacementAction.java
@@ -1,22 +1,5 @@
package com.github.simiacryptus.aicoder.util;
-import com.github.simiacryptus.aicoder.openai.CompletionRequest;
-import com.github.simiacryptus.aicoder.openai.ModerationException;
-import com.intellij.openapi.actionSystem.AnAction;
-import com.intellij.openapi.actionSystem.AnActionEvent;
-import com.intellij.openapi.actionSystem.CommonDataKeys;
-import com.intellij.openapi.editor.Caret;
-import com.intellij.openapi.editor.CaretModel;
-import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.util.NlsActions;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import javax.swing.*;
-import java.io.IOException;
-
-import static com.github.simiacryptus.aicoder.util.UITools.replaceString;
-
/**
* TextReplacementAction is an abstract class that extends the AnAction class.
* It provides a static method create() that takes in a text, description, icon, and an ActionTextEditorFunction.
@@ -25,53 +8,8 @@
* It then calls the edit() method, which is implemented by the subclasses, and replaces the selected text with the new text.
* The ActionTextEditorFunction is a functional interface that takes in an AnActionEvent and a String and returns a String.
*/
-public class TextReplacementAction extends AnAction {
-
- private final ActionTextEditorFunction fn;
-
- private TextReplacementAction(@Nullable @NlsActions.ActionText CharSequence text, @Nullable @NlsActions.ActionDescription CharSequence description, @Nullable Icon icon, @NotNull ActionTextEditorFunction fn) {
- super(text.toString(), description.toString(), icon);
- this.fn = fn;
- }
-
- public static @NotNull TextReplacementAction create(@Nullable @NlsActions.ActionText CharSequence text, @Nullable @NlsActions.ActionDescription CharSequence description, @Nullable Icon icon, @NotNull ActionTextEditorFunction fn) {
- return new TextReplacementAction(text, description, icon, fn);
- }
-
- @Override
- public void actionPerformed(@NotNull final AnActionEvent e) {
- final Editor editor = e.getRequiredData(CommonDataKeys.EDITOR);
- final CaretModel caretModel = editor.getCaretModel();
- final Caret primaryCaret = caretModel.getPrimaryCaret();
- try {
- int selectionStart = primaryCaret.getSelectionStart();
- int selectionEnd = primaryCaret.getSelectionEnd();
- String selectedText = primaryCaret.getSelectedText();
- CompletionRequest request = fn.apply(e, selectedText);
- Caret caret = e.getData(CommonDataKeys.CARET);
- CharSequence indent = UITools.getIndent(caret);
- UITools.redoableRequest(request, indent, e, (CharSequence x) -> {
- CharSequence newText = fn.postTransform(e, selectedText, x);
- return replaceString(editor.getDocument(), selectionStart, selectionEnd, newText);
- });
- } catch (ModerationException | IOException ex) {
- UITools.handle(ex);
- }
- }
-
- public interface ActionTextEditorFunction {
- CompletionRequest apply(AnActionEvent actionEvent, String input) throws IOException, ModerationException;
+@SuppressWarnings("unused")
+public class TextReplacementAction {
- /**
- *
- * Override this method to post-transform the completion string.
- *
- * @param event The action event
- * @param prompt The prompt string
- * @param completion The completion string
- * @return The transformed string
- */
- default CharSequence postTransform(AnActionEvent event, CharSequence prompt, CharSequence completion) { return completion; }
- }
}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/util/UITools.java b/src/main/java/com/github/simiacryptus/aicoder/util/UITools.java
index 32af4f43..5ff21911 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/util/UITools.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/util/UITools.java
@@ -1,6 +1,7 @@
package com.github.simiacryptus.aicoder.util;
import com.github.simiacryptus.aicoder.config.AppSettingsState;
+import com.github.simiacryptus.aicoder.config.Name;
import com.github.simiacryptus.aicoder.openai.CompletionRequest;
import com.github.simiacryptus.aicoder.openai.ModerationException;
import com.github.simiacryptus.aicoder.openai.OpenAI_API;
@@ -17,18 +18,33 @@
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.util.TextRange;
+import com.intellij.ui.components.JBLabel;
+import com.intellij.util.ui.FormBuilder;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import javax.swing.*;
-import java.util.concurrent.ConcurrentLinkedDeque;
+import javax.swing.text.JTextComponent;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.Set;
+import java.util.WeakHashMap;
import java.util.function.Function;
+import java.util.stream.Collectors;
public class UITools {
private static final Logger log = Logger.getInstance(UITools.class);
- public static final ConcurrentLinkedDeque retry = new ConcurrentLinkedDeque<>();
+ public static final WeakHashMap retry = new WeakHashMap<>();
+
+ public static void redoableRequest(@NotNull CompletionRequest request, CharSequence indent, @NotNull AnActionEvent event, @NotNull Function action) {
+ redoableRequest(request, indent, event, x->x, action);
+ }
/**
* This method is responsible for making a redoable request.
@@ -39,33 +55,30 @@ public class UITools {
* @param action The action to be taken when the request is completed.
* @return A {@link Runnable} that can be used to redo the request.
*/
- public static void redoableRequest(CompletionRequest request, CharSequence indent, @NotNull AnActionEvent event, Function action) {
+ public static void redoableRequest(@NotNull CompletionRequest request, CharSequence indent, @NotNull AnActionEvent event, @NotNull Function transformCompletion, @NotNull Function action) {
Editor editor = event.getData(CommonDataKeys.EDITOR);
- Document document = editor.getDocument();
- //document.setReadOnly(true);
+ Document document = Objects.requireNonNull(editor).getDocument();
ProgressManager progressManager = ProgressManager.getInstance();
ProgressIndicator progressIndicator = progressManager.getProgressIndicator();
if(null != progressIndicator) {
progressIndicator.setIndeterminate(true);
progressIndicator.setText("Talking to OpenAI...");
}
- ListenableFuture resultFuture = request.complete(event.getProject(), indent);
- Futures.addCallback(resultFuture, new FutureCallback() {
+ ListenableFuture resultFuture = OpenAI_API.INSTANCE.complete(event.getProject(), request, indent);
+ Futures.addCallback(resultFuture, new FutureCallback<>() {
@Override
- public void onSuccess(CharSequence result) {
- //document.setReadOnly(false);
- if(null != progressIndicator) {
+ public void onSuccess(@NotNull CharSequence result) {
+ if (null != progressIndicator) {
progressIndicator.cancel();
}
WriteCommandAction.runWriteCommandAction(event.getProject(), () -> {
- retry.add(getRetry(request, indent, event, action, action.apply(result.toString())));
+ retry.put(document, getRetry(request, indent, event, action, action.apply(transformCompletion.apply(result.toString()))));
});
}
@Override
public void onFailure(Throwable t) {
- //document.setReadOnly(false);
- if(null != progressIndicator) {
+ if (null != progressIndicator) {
progressIndicator.cancel();
}
handle(t);
@@ -88,32 +101,29 @@ public void onFailure(Throwable t) {
* @return a {@link Runnable} that will attempt to complete the given {@link CompletionRequest}
*/
@NotNull
- private static Runnable getRetry(CompletionRequest request, CharSequence indent, AnActionEvent event, Function action, Runnable undo) {
- Document document = event.getData(CommonDataKeys.EDITOR).getDocument();
- //document.setReadOnly(true);
+ private static Runnable getRetry(@NotNull CompletionRequest request, CharSequence indent, @NotNull AnActionEvent event, @NotNull Function action, @Nullable Runnable undo) {
+ Document document = Objects.requireNonNull(event.getData(CommonDataKeys.EDITOR)).getDocument();
ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
if(null != progressIndicator) {
progressIndicator.setIndeterminate(true);
}
return () -> {
- ListenableFuture retryFuture = request.complete(event.getProject(), indent);
- Futures.addCallback(retryFuture, new FutureCallback() {
+ ListenableFuture retryFuture = OpenAI_API.INSTANCE.complete(event.getProject(), request, indent);
+ Futures.addCallback(retryFuture, new FutureCallback<>() {
@Override
- public void onSuccess(CharSequence result) {
+ public void onSuccess(@NotNull CharSequence result) {
WriteCommandAction.runWriteCommandAction(event.getProject(), () -> {
- //document.setReadOnly(false);
- if(null != progressIndicator) {
+ if (null != progressIndicator) {
progressIndicator.cancel();
}
if (null != undo) undo.run();
- retry.add(getRetry(request, indent, event, action, action.apply(result.toString())));
+ retry.put(document, getRetry(request, indent, event, action, action.apply(result.toString())));
});
}
@Override
public void onFailure(Throwable t) {
- //document.setReadOnly(false);
- if(null != progressIndicator) {
+ if (null != progressIndicator) {
progressIndicator.cancel();
}
handle(t);
@@ -143,7 +153,7 @@ public static String getInstruction(String instruction) {
* @param newText The new string to replace the old string.
* @return A Runnable that can be used to undo the replacement.
*/
- public static Runnable replaceString(Document document, int startOffset, int endOffset, CharSequence newText) {
+ public static @NotNull Runnable replaceString(@NotNull Document document, int startOffset, int endOffset, @NotNull CharSequence newText) {
CharSequence oldText = document.getText(new TextRange(startOffset, endOffset));
document.replaceString(startOffset, endOffset, newText);
return () -> {
@@ -161,7 +171,7 @@ public static Runnable replaceString(Document document, int startOffset, int end
* @param newText The string to insert.
* @return A Runnable that can be used to undo the insertion.
*/
- public static Runnable insertString(Document document, int startOffset, CharSequence newText) {
+ public static @NotNull Runnable insertString(@NotNull Document document, int startOffset, @NotNull CharSequence newText) {
document.insertString(startOffset, newText);
return () -> {
if (!document.getText(new TextRange(startOffset, startOffset + newText.length())).equals(newText))
@@ -170,7 +180,8 @@ public static Runnable insertString(Document document, int startOffset, CharSequ
};
}
- public static Runnable deleteString(Document document, int startOffset, int endOffset) {
+ @SuppressWarnings("unused")
+ public static @NotNull Runnable deleteString(@NotNull Document document, int startOffset, int endOffset) {
CharSequence oldText = document.getText(new TextRange(startOffset, endOffset));
document.deleteString(startOffset, endOffset);
return () -> {
@@ -178,12 +189,16 @@ public static Runnable deleteString(Document document, int startOffset, int endO
};
}
- public static CharSequence getIndent(Caret caret) {
+ public static CharSequence getIndent(@Nullable Caret caret) {
if (null == caret) return "";
Document document = caret.getEditor().getDocument();
- return IndentedText.fromString(document.getText().split("\n")[document.getLineNumber(caret.getSelectionStart())]).getIndent();
+ String documentText = document.getText();
+ int lineNumber = document.getLineNumber(caret.getSelectionStart());
+ String[] lines = documentText.split("\n");
+ return IndentedText.fromString(lines[Math.min(Math.max(lineNumber, 0), lines.length-1)]).getIndent();
}
+ @SuppressWarnings("unused")
public static boolean hasSelection(@NotNull AnActionEvent e) {
Caret caret = e.getData(CommonDataKeys.CARET);
return null != caret && caret.hasSelection();
@@ -194,7 +209,7 @@ public static void handle(@NotNull Throwable ex) {
JOptionPane.showMessageDialog(null, ex.getMessage(), "Warning", JOptionPane.WARNING_MESSAGE);
}
- public static CharSequence getIndent(AnActionEvent event) {
+ public static CharSequence getIndent(@NotNull AnActionEvent event) {
Caret caret = event.getData(CommonDataKeys.CARET);
CharSequence indent;
if (null == caret) {
@@ -205,20 +220,159 @@ public static CharSequence getIndent(AnActionEvent event) {
return indent;
}
- public static String queryAPIKey() {
+ public static @Nullable String queryAPIKey() {
JPanel panel = new JPanel();
JLabel label = new JLabel("Enter OpenAI API Key:");
JPasswordField pass = new JPasswordField(100);
panel.add(label);
panel.add(pass);
- CharSequence[] options = new CharSequence[]{"OK", "Cancel"};
- int option = JOptionPane.showOptionDialog(null, panel, "API Key",
- JOptionPane.NO_OPTION, JOptionPane.PLAIN_MESSAGE,
- null, options, options[1]);
- if (option == 0) {
+ Object[] options = {"OK", "Cancel"};
+ if (JOptionPane.showOptionDialog(
+ null,
+ panel,
+ "API Key",
+ JOptionPane.NO_OPTION,
+ JOptionPane.PLAIN_MESSAGE,
+ null,
+ options,
+ options[1]) == JOptionPane.OK_OPTION) {
char[] password = pass.getPassword();
return new String(password);
+ } else {
+ return null;
+ }
+ }
+
+ public static void readUI(@NotNull Object component, @NotNull T settings) {
+ Class> componentClass = component.getClass();
+ Set declaredUIFields = Arrays.stream(componentClass.getFields()).map(Field::getName).collect(Collectors.toSet());
+ for (Field settingsField : settings.getClass().getFields()) {
+ settingsField.setAccessible(true);
+ String settingsFieldName = settingsField.getName();
+ try {
+ Object newSettingsValue = null;
+ if(!declaredUIFields.contains(settingsFieldName)) continue;
+ Field uiField = componentClass.getDeclaredField(settingsFieldName);
+ Object uiFieldVal = uiField.get(component);
+ switch (settingsField.getType().getName()) {
+ case "java.lang.String":
+ if (uiFieldVal instanceof JTextComponent) {
+ newSettingsValue = ((JTextComponent) uiFieldVal).getText();
+ } else if (uiFieldVal instanceof ComboBox) {
+ newSettingsValue = ((ComboBox) uiFieldVal).getItem();
+ }
+ break;
+ case "int":
+ if (uiFieldVal instanceof JTextComponent) {
+ newSettingsValue = Integer.parseInt(((JTextComponent) uiFieldVal).getText());
+ }
+ break;
+ case "long":
+ if (uiFieldVal instanceof JTextComponent) {
+ newSettingsValue = Long.parseLong(((JTextComponent) uiFieldVal).getText());
+ }
+ break;
+ case "double":
+ if (uiFieldVal instanceof JTextComponent) {
+ newSettingsValue = Double.parseDouble(((JTextComponent) uiFieldVal).getText());
+ }
+ break;
+ case "boolean":
+ if (uiFieldVal instanceof JCheckBox) {
+ newSettingsValue = ((JCheckBox) uiFieldVal).isSelected();
+ } else if (uiFieldVal instanceof JTextComponent) {
+ newSettingsValue = Boolean.parseBoolean(((JTextComponent) uiFieldVal).getText());
+ }
+ break;
+ default:
+
+ if (Enum.class.isAssignableFrom(settingsField.getType())) {
+ if (uiFieldVal instanceof ComboBox) {
+ ComboBox comboBox = (ComboBox) uiFieldVal;
+ CharSequence item = comboBox.getItem();
+ newSettingsValue = Enum.valueOf((Class extends Enum>) settingsField.getType(), item.toString());
+ }
+ }
+ break;
+ }
+ settingsField.set(settings, newSettingsValue);
+ } catch (Throwable e) {
+ new RuntimeException("Error processing " + settingsField, e).printStackTrace();
+ }
+ }
+ }
+
+ public static void writeUI(@NotNull Object component, @NotNull T settings) {
+ Class> componentClass = component.getClass();
+ Set declaredUIFields = Arrays.stream(componentClass.getFields()).map(Field::getName).collect(Collectors.toSet());
+ for (Field settingsField : settings.getClass().getFields()) {
+ String fieldName = settingsField.getName();
+ try {
+ if(!declaredUIFields.contains(fieldName)) continue;
+ Field uiField = componentClass.getDeclaredField(fieldName);
+ Object settingsVal = settingsField.get(settings);
+ if(null == settingsVal) continue;
+ Object uiVal = uiField.get(component);
+ switch (settingsField.getType().getName()) {
+ case "java.lang.String":
+ if (uiVal instanceof JTextComponent) {
+ ((JTextComponent) uiVal).setText((String) settingsVal);
+ } else if (uiVal instanceof ComboBox) {
+ ((ComboBox) uiVal).setItem(settingsVal.toString());
+ }
+ break;
+ case "int":
+ if (uiVal instanceof JTextComponent) {
+ ((JTextComponent) uiVal).setText(Integer.toString((Integer) settingsVal));
+ }
+ break;
+ case "long":
+ if (uiVal instanceof JTextComponent) {
+ ((JTextComponent) uiVal).setText(Long.toString((Integer) settingsVal));
+ }
+ break;
+ case "double":
+ if (uiVal instanceof JTextComponent) {
+ ((JTextComponent) uiVal).setText(Double.toString(((Double) settingsVal)));
+ }
+ break;
+ case "boolean":
+ if (uiVal instanceof JCheckBox) {
+ ((JCheckBox) uiVal).setSelected(((Boolean) settingsVal));
+ } else if (uiVal instanceof JTextComponent) {
+ ((JTextComponent) uiVal).setText(Boolean.toString((Boolean) settingsVal));
+ }
+ break;
+ default:
+ if (uiVal instanceof ComboBox) {
+ ((ComboBox) uiVal).setItem(settingsVal.toString());
+ }
+ break;
+ }
+ } catch (Throwable e) {
+ new RuntimeException("Error processing " + settingsField, e).printStackTrace();
+ }
+ }
+ }
+
+ public static void addFields(@NotNull Object ui, @NotNull FormBuilder formBuilder) {
+ for (Field field : ui.getClass().getFields()) {
+ if(Modifier.isStatic(field.getModifiers())) continue;
+ try {
+ Name nameAnnotation = field.getDeclaredAnnotation(Name.class);
+ JComponent component = (JComponent) field.get(ui);
+ if (null == component) continue;
+ if (nameAnnotation != null) {
+ formBuilder.addLabeledComponent(new JBLabel(nameAnnotation.value() + ": "), component, 1, false);
+ } else {
+ formBuilder.addComponentToRightColumn(component, 1);
+ }
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (Throwable e) {
+ log.warn("Error processing " + field.getName(), e);
+ }
}
- return null;
}
+
}
diff --git a/src/main/java/com/github/simiacryptus/aicoder/psi/PsiClassContext.java b/src/main/java/com/github/simiacryptus/aicoder/util/psi/PsiClassContext.java
similarity index 96%
rename from src/main/java/com/github/simiacryptus/aicoder/psi/PsiClassContext.java
rename to src/main/java/com/github/simiacryptus/aicoder/util/psi/PsiClassContext.java
index 049b81bc..15db4119 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/psi/PsiClassContext.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/util/psi/PsiClassContext.java
@@ -1,4 +1,4 @@
-package com.github.simiacryptus.aicoder.psi;
+package com.github.simiacryptus.aicoder.util.psi;
import com.github.simiacryptus.aicoder.util.StringTools;
import com.intellij.openapi.util.TextRange;
@@ -62,7 +62,7 @@ public void visitElement(@NotNull PsiElement element) {
currentContext.children.add(new PsiClassContext(text.trim(), isPrior, isOverlap));
} else if (simpleName.equals("PsiCommentImpl") || simpleName.equals("PsiDocCommentImpl")) {
if (within) {
- currentContext.children.add(new PsiClassContext(indent + text.trim(), isPrior, isOverlap));
+ currentContext.children.add(new PsiClassContext(indent + text.trim(), false, true));
}
} else if (simpleName.equals("PsiMethodImpl") || simpleName.equals("PsiFieldImpl")) {
String declaration = text;
@@ -94,7 +94,7 @@ public void visitElement(@NotNull PsiElement element) {
indent = prevIndent;
currentContext = prevclassBuffer;
if (!isOverlap) {
- currentContext.children.add(new PsiClassContext("}", isPrior, isOverlap));
+ currentContext.children.add(new PsiClassContext("}", isPrior, false));
}
} else if (!isOverlap && Arrays.asList("PsiCodeBlockImpl", "PsiForStatementImpl").contains(simpleName)) {
// Skip
diff --git a/src/main/java/com/github/simiacryptus/aicoder/psi/PsiMarkdownContext.java b/src/main/java/com/github/simiacryptus/aicoder/util/psi/PsiMarkdownContext.java
similarity index 94%
rename from src/main/java/com/github/simiacryptus/aicoder/psi/PsiMarkdownContext.java
rename to src/main/java/com/github/simiacryptus/aicoder/util/psi/PsiMarkdownContext.java
index 3f111fbe..c4b77aea 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/psi/PsiMarkdownContext.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/util/psi/PsiMarkdownContext.java
@@ -1,4 +1,4 @@
-package com.github.simiacryptus.aicoder.psi;
+package com.github.simiacryptus.aicoder.util.psi;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
@@ -27,7 +27,7 @@ public static PsiMarkdownContext getContext(@NotNull PsiFile psiFile, int select
}
public int getEnd() {
- return Math.max(start + text.length(), children.stream().mapToInt(x -> x.getEnd()).max().orElse(0));
+ return Math.max(start + text.length(), children.stream().mapToInt(PsiMarkdownContext::getEnd).max().orElse(0));
}
public int headerLevel() {
@@ -40,6 +40,7 @@ public int headerLevel() {
final CharSequence indent = "";
@NotNull PsiMarkdownContext section = PsiMarkdownContext.this;
+ @SuppressWarnings("unused")
@Override
public void visitElement(@NotNull PsiElement element) {
String text = element.getText();
diff --git a/src/main/java/com/github/simiacryptus/aicoder/psi/PsiUtil.java b/src/main/java/com/github/simiacryptus/aicoder/util/psi/PsiUtil.java
similarity index 71%
rename from src/main/java/com/github/simiacryptus/aicoder/psi/PsiUtil.java
rename to src/main/java/com/github/simiacryptus/aicoder/util/psi/PsiUtil.java
index 423f9981..fe077fed 100644
--- a/src/main/java/com/github/simiacryptus/aicoder/psi/PsiUtil.java
+++ b/src/main/java/com/github/simiacryptus/aicoder/util/psi/PsiUtil.java
@@ -1,16 +1,18 @@
-package com.github.simiacryptus.aicoder.psi;
+package com.github.simiacryptus.aicoder.util.psi;
import com.github.simiacryptus.aicoder.util.StringTools;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.editor.Caret;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
+import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
public class PsiUtil {
@@ -28,13 +30,12 @@ public static PsiElement getLargestIntersectingComment(@NotNull PsiElement eleme
* @param types The types of elements to search for.
* @return The largest element that intersects with the given selection range.
*/
- public static PsiElement getLargestIntersecting(@NotNull PsiElement element, int selectionStart, int selectionEnd, CharSequence... types) {
+ public static PsiElement getLargestIntersecting(@NotNull PsiElement element, int selectionStart, int selectionEnd, CharSequence @NotNull ... types) {
final AtomicReference largest = new AtomicReference<>(null);
final AtomicReference visitor = new AtomicReference<>();
visitor.set(new PsiElementVisitor() {
@Override
public void visitElement(@NotNull PsiElement element) {
- if (null == element) return;
TextRange textRange = element.getTextRange();
boolean within = (textRange.getStartOffset() <= selectionStart && textRange.getEndOffset() + 1 >= selectionStart && textRange.getStartOffset() <= selectionEnd && textRange.getEndOffset() + 1 >= selectionEnd);
CharSequence simpleName = element.getClass().getSimpleName();
@@ -50,13 +51,13 @@ public void visitElement(@NotNull PsiElement element) {
element.accept(visitor.get());
return largest.get();
}
- public static List getAll(@NotNull PsiElement element, CharSequence... types) {
+
+ public static @NotNull List getAll(@NotNull PsiElement element, CharSequence @NotNull ... types) {
final List elements = new ArrayList<>();
final AtomicReference visitor = new AtomicReference<>();
visitor.set(new PsiElementVisitor() {
@Override
public void visitElement(@NotNull PsiElement element) {
- if (null == element) return;
if (Arrays.asList(expand(types)).contains(element.getClass().getSimpleName())) {
elements.add(element);
} else {
@@ -90,13 +91,12 @@ public static PsiElement getSmallestIntersecting(@NotNull PsiElement element, in
* @param types The types of the elements to be retrieved.
* @return The smallest intersecting entity from the given PsiElement.
*/
- public static PsiElement getSmallestIntersecting(@NotNull PsiElement element, int selectionStart, int selectionEnd, CharSequence... types) {
+ public static PsiElement getSmallestIntersecting(@NotNull PsiElement element, int selectionStart, int selectionEnd, CharSequence @NotNull ... types) {
final AtomicReference largest = new AtomicReference<>(null);
final AtomicReference visitor = new AtomicReference<>();
visitor.set(new PsiElementVisitor() {
@Override
public void visitElement(@NotNull PsiElement element) {
- if (null == element) return;
TextRange textRange = element.getTextRange();
boolean within = (textRange.getStartOffset() <= selectionStart && textRange.getEndOffset() + 1 >= selectionStart && textRange.getStartOffset() <= selectionEnd && textRange.getEndOffset() + 1 >= selectionEnd);
CharSequence simpleName = element.getClass().getSimpleName();
@@ -114,15 +114,15 @@ public void visitElement(@NotNull PsiElement element) {
return largest.get();
}
- private static CharSequence[] expand(CharSequence[] types) {
- return Arrays.stream(types).flatMap(x-> Stream.of(x, StringTools.stripSuffix(x, "Impl"))).distinct().toArray(CharSequence[]::new);
+ private static CharSequence @NotNull [] expand(CharSequence @NotNull [] types) {
+ return Arrays.stream(types).flatMap(x -> Stream.of(x, StringTools.stripSuffix(x, "Impl"))).distinct().toArray(CharSequence[]::new);
}
- public static PsiElement getFirstBlock(@NotNull PsiElement element, CharSequence blockType) {
+ public static @Nullable PsiElement getFirstBlock(@NotNull PsiElement element, CharSequence blockType) {
PsiElement[] children = element.getChildren();
- if(null == children || 0 == children.length) return null;
+ if (0 == children.length) return null;
PsiElement first = children[0];
- if(first.getClass().getSimpleName().equals(blockType)) return first;
+ if (first.getClass().getSimpleName().equals(blockType)) return first;
return null;
}
@@ -143,8 +143,7 @@ public void visitElement(@NotNull PsiElement element) {
}
});
element.accept(visitor.get());
- PsiElement psiElement = largest.get();
- return psiElement;
+ return largest.get();
}
/**
@@ -155,6 +154,7 @@ public void visitElement(@NotNull PsiElement element) {
* @return A {@link HashSet} of {@link String}s containing the simple names of all the {@link PsiElement}s contained
* within the given {@link PsiElement}.
*/
+ @SuppressWarnings("unused")
public static @NotNull HashSet getAllElementNames(@NotNull PsiElement element) {
HashSet set = new HashSet<>();
AtomicReference visitor = new AtomicReference<>();
@@ -180,21 +180,45 @@ private static void printTree(@NotNull PsiElement element, @NotNull StringBuilde
for (int i = 0; i < level; i++) {
builder.append(" ");
}
- builder.append(element.getClass().getSimpleName() + " " + element.getText().replaceAll("\n", "\\\\n"));
+ Class extends @NotNull PsiElement> elementClass = element.getClass();
+ String simpleName = getName(elementClass);
+ builder.append(simpleName + " " + element.getText().replaceAll("\n", "\\\\n"));
builder.append("\n");
for (PsiElement child : element.getChildren()) {
printTree(child, builder, level + 1);
}
}
- public static PsiElement getLargestContainedEntity(PsiElement element, int selectionStart, int selectionEnd) {
- if(null == element) return element;
+ @NotNull
+ private static String getName(Class> elementClass) {
+ StringBuilder stringBuilder = new StringBuilder();
+ Set interfaces = getInterfaces(elementClass);
+ while(elementClass != Object.class) {
+ if(stringBuilder.length()>0) stringBuilder.append("/");
+ stringBuilder.append(elementClass.getSimpleName());
+ elementClass = elementClass.getSuperclass();
+ }
+ stringBuilder.append("[ ");
+ stringBuilder.append(interfaces.stream().sorted().collect(Collectors.joining(",")));
+ stringBuilder.append("]");
+ return stringBuilder.toString();
+ }
+
+ @NotNull
+ private static Set getInterfaces(Class> elementClass) {
+ HashSet strings = Arrays.stream(elementClass.getInterfaces()).map(Class::getSimpleName).collect(Collectors.toCollection(HashSet::new));
+ if (elementClass.getSuperclass() != Object.class) strings.addAll(getInterfaces(elementClass.getSuperclass()));
+ return strings;
+ }
+
+ public static PsiElement getLargestContainedEntity(@Nullable PsiElement element, int selectionStart, int selectionEnd) {
+ if (null == element) return null;
TextRange textRange = element.getTextRange();
- if(textRange.getStartOffset() >= selectionStart && textRange.getEndOffset() <= selectionEnd) return element;
+ if (textRange.getStartOffset() >= selectionStart && textRange.getEndOffset() <= selectionEnd) return element;
PsiElement largestContainedChild = null;
for (PsiElement child : element.getChildren()) {
PsiElement entity = getLargestContainedEntity(child, selectionStart, selectionEnd);
- if(null != entity) {
+ if (null != entity) {
if (largestContainedChild == null || largestContainedChild.getTextRange().getLength() < entity.getTextRange().getLength()) {
largestContainedChild = entity;
}
@@ -203,4 +227,16 @@ public static PsiElement getLargestContainedEntity(PsiElement element, int selec
return largestContainedChild;
}
+ @Nullable
+ public static PsiElement getPsiFile(@NotNull AnActionEvent e) {
+ Caret caret = e.getData(CommonDataKeys.CARET);
+ if (null == caret) return null;
+ PsiElement psiFile = e.getData(CommonDataKeys.PSI_FILE);
+ if (null == psiFile) return null;
+ int selectionStart = caret.getSelectionStart();
+ int selectionEnd = caret.getSelectionEnd();
+ PsiElement largestContainedEntity = getLargestContainedEntity(psiFile, selectionStart, selectionEnd);
+ if (largestContainedEntity != null) psiFile = largestContainedEntity;
+ return psiFile;
+ }
}
diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml
index a0fb9d23..a1fb9945 100644
--- a/src/main/resources/META-INF/plugin.xml
+++ b/src/main/resources/META-INF/plugin.xml
@@ -18,15 +18,125 @@
SimiaCryptus Software
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+