diff --git a/pom.xml b/pom.xml index ac03fdc..746884e 100644 --- a/pom.xml +++ b/pom.xml @@ -147,6 +147,7 @@ io.github.jeddict.ai + io.github.jeddict.ai.lang false diff --git a/src/main/java/io/github/jeddict/ai/completion/JeddictCompletionProvider.java b/src/main/java/io/github/jeddict/ai/completion/JeddictCompletionProvider.java index 2b64ff8..844830f 100644 --- a/src/main/java/io/github/jeddict/ai/completion/JeddictCompletionProvider.java +++ b/src/main/java/io/github/jeddict/ai/completion/JeddictCompletionProvider.java @@ -48,7 +48,7 @@ import com.sun.source.tree.Tree; import com.sun.source.tree.VariableTree; import com.sun.source.util.DocTrees; -import io.github.jeddict.ai.Snippet; +import io.github.jeddict.ai.lang.Snippet; import static io.github.jeddict.ai.scanner.ProjectClassScanner.getFileObjectFromEditor; import io.github.jeddict.ai.scanner.ClassData; import java.util.stream.Collectors; diff --git a/src/main/java/io/github/jeddict/ai/components/AssistantTopComponent.java b/src/main/java/io/github/jeddict/ai/components/AssistantTopComponent.java index 8b77de6..de5f01a 100644 --- a/src/main/java/io/github/jeddict/ai/components/AssistantTopComponent.java +++ b/src/main/java/io/github/jeddict/ai/components/AssistantTopComponent.java @@ -22,6 +22,7 @@ import static io.github.jeddict.ai.util.EditorUtil.isSuitableForWebAppDirectory; import static io.github.jeddict.ai.util.StringUtil.convertToCapitalized; import java.awt.BorderLayout; +import java.awt.Color; import java.awt.Desktop; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; @@ -114,6 +115,14 @@ public JEditorPane createHtmlPane(String content) { parentPanel.add(editorPane); return editorPane; } + + public JEditorPane createPane() { + JEditorPane editorPane = new JEditorPane(); + editorPane.setEditable(false); + editorPane.setBackground(Color.WHITE); + parentPanel.add(editorPane); + return editorPane; + } public JEditorPane createCodePane(String content) { JEditorPane editorPane = new JEditorPane(); diff --git a/src/main/java/io/github/jeddict/ai/hints/ExpressionFix.java b/src/main/java/io/github/jeddict/ai/hints/ExpressionFix.java index abac2bc..bc3cb9e 100644 --- a/src/main/java/io/github/jeddict/ai/hints/ExpressionFix.java +++ b/src/main/java/io/github/jeddict/ai/hints/ExpressionFix.java @@ -22,8 +22,9 @@ import com.sun.source.tree.ExpressionTree; import com.sun.source.tree.Tree; import com.sun.source.util.TreePath; +import io.github.jeddict.ai.JeddictUpdateManager; import io.github.jeddict.ai.completion.Action; -import io.github.jeddict.ai.JeddictChatModel; +import io.github.jeddict.ai.lang.JeddictChatModel; import io.github.jeddict.ai.util.StringUtil; import org.netbeans.api.java.source.JavaSource; import org.netbeans.api.java.source.TreePathHandle; @@ -48,7 +49,7 @@ public ExpressionFix(TreePathHandle tpHandle, Action action, TreePath treePath) @Override protected String getText() { - return NbBundle.getMessage(JeddictChatModel.class, "HINT_ENHANCE_EXPRESSION"); + return NbBundle.getMessage(JeddictUpdateManager.class, "HINT_ENHANCE_EXPRESSION"); } @Override diff --git a/src/main/java/io/github/jeddict/ai/hints/JavaDocFixImpl.java b/src/main/java/io/github/jeddict/ai/hints/JavaDocFixImpl.java index f1bb227..1069cf6 100644 --- a/src/main/java/io/github/jeddict/ai/hints/JavaDocFixImpl.java +++ b/src/main/java/io/github/jeddict/ai/hints/JavaDocFixImpl.java @@ -18,9 +18,7 @@ */ package io.github.jeddict.ai.hints; -import io.github.jeddict.ai.util.StringUtil; import com.sun.source.doctree.DocCommentTree; -import com.sun.source.doctree.DocTree; import com.sun.source.tree.CompilationUnitTree; import com.sun.source.tree.MethodTree; import com.sun.source.tree.Tree; @@ -31,21 +29,18 @@ import com.sun.source.tree.VariableTree; import com.sun.source.util.DocTrees; import com.sun.source.util.TreePath; +import io.github.jeddict.ai.JeddictUpdateManager; import io.github.jeddict.ai.completion.Action; -import io.github.jeddict.ai.JeddictChatModel; import static io.github.jeddict.ai.completion.Action.ENHANCE; +import io.github.jeddict.ai.lang.JeddictChatModel; import static io.github.jeddict.ai.util.SourceUtil.geIndentaion; +import io.github.jeddict.ai.util.StringUtil; import static io.github.jeddict.ai.util.StringUtil.removeCodeBlockMarkers; -import static io.github.jeddict.ai.util.StringUtil.trimLeadingSpaces; -import java.util.LinkedList; -import java.util.List; import javax.lang.model.element.Element; -import javax.lang.model.element.ElementKind; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import org.netbeans.api.java.source.ElementHandle; import org.netbeans.api.java.source.JavaSource; -import org.netbeans.api.java.source.TreeMaker; import org.netbeans.api.java.source.TreePathHandle; import org.netbeans.api.java.source.WorkingCopy; import org.netbeans.spi.java.hints.JavaFix; @@ -71,9 +66,9 @@ public JavaDocFixImpl(TreePathHandle tpHandle, Action type, protected String getText() { switch (action) { case ENHANCE: - return NbBundle.getMessage(JeddictChatModel.class, "HINT_JAVADOC_GENERATED", StringUtil.convertToCapitalized(classType.getKind().toString()));//NOI18N + return NbBundle.getMessage(JeddictUpdateManager.class, "HINT_JAVADOC_GENERATED", StringUtil.convertToCapitalized(classType.getKind().toString()));//NOI18N default: - return NbBundle.getMessage(JeddictChatModel.class, "HINT_JAVADOC", StringUtil.convertToCapitalized(classType.getKind().toString()));//NOI18N + return NbBundle.getMessage(JeddictUpdateManager.class, "HINT_JAVADOC", StringUtil.convertToCapitalized(classType.getKind().toString()));//NOI18N } } diff --git a/src/main/java/io/github/jeddict/ai/hints/JeddictHint.java b/src/main/java/io/github/jeddict/ai/hints/JeddictHint.java index f66740d..c39cb29 100644 --- a/src/main/java/io/github/jeddict/ai/hints/JeddictHint.java +++ b/src/main/java/io/github/jeddict/ai/hints/JeddictHint.java @@ -31,7 +31,7 @@ import com.sun.source.util.SourcePositions; import com.sun.source.util.TreePath; import com.sun.source.util.Trees; -import io.github.jeddict.ai.JeddictChatModel; +import io.github.jeddict.ai.JeddictUpdateManager; import io.github.jeddict.ai.completion.Action; import io.github.jeddict.ai.settings.PreferencesManager; import java.util.ArrayList; @@ -189,7 +189,7 @@ public static ErrorDescription run(HintContext ctx) { return null; } Fix[] fixesArray = fixes.toArray(new Fix[0]); // Convert to array - String desc = NbBundle.getMessage(JeddictChatModel.class, "ERR_HINT"); //NOI18N + String desc = NbBundle.getMessage(JeddictUpdateManager.class, "ERR_HINT"); //NOI18N return ErrorDescriptionFactory.forTree(ctx, ctx.getPath(), desc, fixesArray); //NOI18N } diff --git a/src/main/java/io/github/jeddict/ai/hints/LearnFix.java b/src/main/java/io/github/jeddict/ai/hints/LearnFix.java index e86676e..81648ad 100644 --- a/src/main/java/io/github/jeddict/ai/hints/LearnFix.java +++ b/src/main/java/io/github/jeddict/ai/hints/LearnFix.java @@ -26,9 +26,11 @@ import static com.sun.source.tree.Tree.Kind.INTERFACE; import static com.sun.source.tree.Tree.Kind.METHOD; import com.sun.source.util.TreePath; +import dev.langchain4j.data.message.AiMessage; +import dev.langchain4j.model.output.Response; import io.github.jeddict.ai.JeddictUpdateManager; import io.github.jeddict.ai.completion.Action; -import io.github.jeddict.ai.JeddictChatModel; +import io.github.jeddict.ai.lang.JeddictChatModel; import io.github.jeddict.ai.completion.SQLCompletion; import io.github.jeddict.ai.components.AssistantTopComponent; import static io.github.jeddict.ai.components.AssistantTopComponent.attachIcon; @@ -87,6 +89,7 @@ import static io.github.jeddict.ai.components.AssistantTopComponent.saveToEditorIcon; import static io.github.jeddict.ai.components.AssistantTopComponent.settingsIcon; import io.github.jeddict.ai.components.ContextDialog; +import io.github.jeddict.ai.lang.JeddictStreamHandler; import io.github.jeddict.ai.util.EditorUtil; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; @@ -175,13 +178,13 @@ public LearnFix(Action action, FileObject selectedFileObject) { @Override protected String getText() { if (action == Action.LEARN) { - return NbBundle.getMessage(JeddictChatModel.class, "HINT_LEARN", + return NbBundle.getMessage(JeddictUpdateManager.class, "HINT_LEARN", StringUtil.convertToCapitalized(treePath.getLeaf().getKind().toString())); } else if (action == Action.QUERY) { - return NbBundle.getMessage(JeddictChatModel.class, "HINT_QUERY", + return NbBundle.getMessage(JeddictUpdateManager.class, "HINT_QUERY", StringUtil.convertToCapitalized(treePath.getLeaf().getKind().toString())); } else if (action == Action.TEST) { - return NbBundle.getMessage(JeddictChatModel.class, "HINT_TEST", + return NbBundle.getMessage(JeddictUpdateManager.class, "HINT_TEST", StringUtil.convertToCapitalized(treePath.getLeaf().getKind().toString())); } return null; @@ -201,31 +204,44 @@ protected void performRewrite(JavaFix.TransformationContext tc) throws Exception || leaf.getKind() == ENUM || leaf.getKind() == METHOD) { executorService.submit(() -> { - String response; - String fileName = fileObject != null ? fileObject.getName() : null; - if (action == Action.TEST) { - if (leaf instanceof MethodTree) { - response = new JeddictChatModel().generateTestCase(null, null, leaf.toString(), null, null); - } else { - response = new JeddictChatModel().generateTestCase(null, treePath.getCompilationUnit().toString(), null, null, null); - } - } else { - if (leaf instanceof MethodTree) { - response = new JeddictChatModel().assistJavaMethod(leaf.toString()); - } else { - response = new JeddictChatModel().assistJavaClass(treePath.getCompilationUnit().toString()); - } - } String name; if (leaf instanceof MethodTree) { name = ((MethodTree) leaf).getName().toString(); } else { name = ((ClassTree) leaf).getSimpleName().toString(); - } + String fileName = fileObject != null ? fileObject.getName() : null; + SwingUtilities.invokeLater(() -> { - displayHtmlContent(removeCodeBlockMarkers(response), fileName, name + " AI Assistant"); + displayHtmlContent(fileName, name + " AI Assistant"); + JeddictStreamHandler handler = new JeddictStreamHandler(topComponent) { + @Override + public void onComplete(String response) { + sourceCode = EditorUtil.updateEditors(topComponent, response); + responseHistory.add(response); + currentResponseIndex = responseHistory.size() - 1; + } + }; + String response; + if (action == Action.TEST) { + if (leaf instanceof MethodTree) { + response = new JeddictChatModel(handler).generateTestCase(null, null, leaf.toString(), null, null); + } else { + response = new JeddictChatModel(handler).generateTestCase(null, treePath.getCompilationUnit().toString(), null, null, null); + } + } else { + if (leaf instanceof MethodTree) { + response = new JeddictChatModel(handler).assistJavaMethod(leaf.toString()); + } else { + response = new JeddictChatModel(handler).assistJavaClass(treePath.getCompilationUnit().toString()); + } + } + if (response != null && !response.isEmpty()) { + response = removeCodeBlockMarkers(response); + handler.onComplete(response); + } }); + }); } } @@ -337,33 +353,42 @@ public String getFileContext() { public void askQueryForProjectCommit(Project project, String commitChanges, String intitalCommitMessage) { ProjectInformation info = ProjectUtils.getInformation(project); String projectName = info.getDisplayName(); - String response = new JeddictChatModel().generateCommitMessageSuggestions(commitChanges, intitalCommitMessage); - displayHtmlContent(removeCodeBlockMarkers(response), null, projectName + " GenAI Commit"); - this.commitChanges = commitChanges; - } - - public void displayHtmlContent(final String response, String filename, String title) { SwingUtilities.invokeLater(() -> { - Preferences prefs = Preferences.userNodeForPackage(AssistantTopComponent.class); - prefs.putBoolean(AssistantTopComponent.PREFERENCE_KEY, true); - topComponent = new AssistantTopComponent(title, null, getProject()); - sourceCode = EditorUtil.updateEditors(topComponent, response); - - JScrollPane scrollPane = new JScrollPane(topComponent.getParentPanel()); - topComponent.add(scrollPane, BorderLayout.CENTER); - - responseHistory.add(response); - currentResponseIndex = responseHistory.size() - 1; + displayHtmlContent(null, projectName + " GenAI Commit"); + JeddictStreamHandler handler = new JeddictStreamHandler(topComponent) { + @Override + public void onComplete(String response) { + response = removeCodeBlockMarkers(response); + sourceCode = EditorUtil.updateEditors(topComponent, response); + responseHistory.add(response); + currentResponseIndex = responseHistory.size() - 1; + LearnFix.this.commitChanges = commitChanges; + } + }; + String response = new JeddictChatModel(handler).generateCommitMessageSuggestions(commitChanges, intitalCommitMessage); + if (response != null && !response.isEmpty()) { + response = removeCodeBlockMarkers(response); + handler.onComplete(response); + } + }); + } - topComponent.add(createBottomPanel(null, filename, title, null), BorderLayout.SOUTH); - topComponent.open(); - topComponent.requestActive(); - Runtime.getRuntime().addShutdownHook(new Thread(() -> { + public void displayHtmlContent(String filename, String title) { + Preferences prefs = Preferences.userNodeForPackage(AssistantTopComponent.class); + prefs.putBoolean(AssistantTopComponent.PREFERENCE_KEY, true); + topComponent = new AssistantTopComponent(title, null, getProject()); + JScrollPane scrollPane = new JScrollPane(topComponent.getParentPanel()); + topComponent.add(scrollPane, BorderLayout.CENTER); + topComponent.add(createBottomPanel(null, filename, title, null), BorderLayout.SOUTH); + topComponent.open(); + topComponent.requestActive(); + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + SwingUtilities.invokeLater(() -> { if (topComponent != null) { topComponent.close(); } - })); - }); + }); + })); } public void openChat(String type, final String query, String fileName, String title, Consumer action) { @@ -465,7 +490,7 @@ private JPanel createBottomPanel(String type, String fileName, String title, Con openInBrowserButton.setMaximumSize(buttonSize); openInBrowserButton.setEnabled(topComponent.getAllEditorCount() > 0); westButtonPanel.add(openInBrowserButton); - + JButton optionsButton = createButton(settingsIcon); // Replace with actual icon path optionsButton.setToolTipText("Open Jeddict AI Assistant Settings"); optionsButton.setPreferredSize(buttonSize); @@ -485,7 +510,6 @@ private JPanel createBottomPanel(String type, String fileName, String title, Con // } // }); // westButtonPanel.add(jeddictButton); - // New Chat Button (West) JButton newChatButton = createButton(newEditorIcon); newChatButton.setToolTipText("Start a new chat"); @@ -613,7 +637,7 @@ private void showFilePathPopup() { boolean enableRules = true; String rules = pm.getCommonPromptRules(); - if(commitChanges != null) { + if (commitChanges != null) { rules = commitChanges; enableRules = false; } @@ -624,11 +648,11 @@ private void showFilePathPopup() { dialog.setSize(800, 800); dialog.setLocationRelativeTo(SwingUtilities.windowForComponent(topComponent)); dialog.setVisible(true); - if(commitChanges == null) { + if (commitChanges == null) { pm.setCommonPromptRules(dialog.getRules()); } } - + private JButton createButton(ImageIcon icon) { JButton button = new JButton(icon); // Set button preferred size to match the icon's size (24x24) @@ -671,42 +695,51 @@ private void handleQuestion(String question, JButton submitButton) { if (prevChat != null) { prevChat = responseHistory.get(currentResponseIndex); } + JeddictStreamHandler handler = new JeddictStreamHandler(topComponent) { + @Override + public void onComplete(String response) { + if (responseHistory.isEmpty() || !response.equals(responseHistory.get(responseHistory.size() - 1))) { + responseHistory.add(response); + currentResponseIndex = responseHistory.size() - 1; + } + String finalResponse = response; + SwingUtilities.invokeLater(() -> { + sourceCode = EditorUtil.updateEditors(topComponent, finalResponse); + submitButton.setIcon(startIcon); + submitButton.setEnabled(true); + saveButton.setEnabled(sourceCode != null); + updateButtons(prevButton, nextButton); + questionPane.setText(""); + }); + } + }; String response; if (sqlCompletion != null) { - response = new JeddictChatModel().assistDbMetadata(sqlCompletion.getMetaData(), question); + response = new JeddictChatModel(handler).assistDbMetadata(sqlCompletion.getMetaData(), question); } else if (commitChanges != null) { - response = new JeddictChatModel().generateCommitMessageSuggestions(commitChanges, question); + response = new JeddictChatModel(handler).generateCommitMessageSuggestions(commitChanges, question); } else if (project != null) { - response = new JeddictChatModel().generateDescription(getProjectContext(), null, null, prevChat, question); + response = new JeddictChatModel(handler).generateDescription(getProjectContext(), null, null, prevChat, question); } else if (selectedFileObjects != null) { - response = new JeddictChatModel().generateDescription(getFilesContext(), null, null, prevChat, question); + response = new JeddictChatModel(handler).generateDescription(getFilesContext(), null, null, prevChat, question); } else if (selectedFileObject != null) { - response = new JeddictChatModel().generateDescription(null, getFileContext(), null, prevChat, question); + response = new JeddictChatModel(handler).generateDescription(null, getFileContext(), null, prevChat, question); } else if (treePath == null) { - response = new JeddictChatModel().generateDescription(null, null, null, prevChat, question); + response = new JeddictChatModel(handler).generateDescription(null, null, null, prevChat, question); } else if (action == Action.TEST) { if (leaf instanceof MethodTree) { - response = new JeddictChatModel().generateTestCase(null, null, leaf.toString(), prevChat, question); + response = new JeddictChatModel(handler).generateTestCase(null, null, leaf.toString(), prevChat, question); } else { - response = new JeddictChatModel().generateTestCase(null, treePath.getCompilationUnit().toString(), null, prevChat, question); + response = new JeddictChatModel(handler).generateTestCase(null, treePath.getCompilationUnit().toString(), null, prevChat, question); } } else { - response = new JeddictChatModel().generateDescription(null, treePath.getCompilationUnit().toString(), treePath.getLeaf() instanceof MethodTree ? treePath.getLeaf().toString() : null, prevChat, question); + response = new JeddictChatModel(handler).generateDescription(null, treePath.getCompilationUnit().toString(), treePath.getLeaf() instanceof MethodTree ? treePath.getLeaf().toString() : null, prevChat, question); } -// response = removeCodeBlockMarkers(response); - if (responseHistory.isEmpty() || !response.equals(responseHistory.get(responseHistory.size() - 1))) { - responseHistory.add(response); - currentResponseIndex = responseHistory.size() - 1; + + if (response != null && !response.isEmpty()) { + response = removeCodeBlockMarkers(response); + handler.onComplete(response); } - String finalResponse = response; - SwingUtilities.invokeLater(() -> { - sourceCode = EditorUtil.updateEditors(topComponent, finalResponse); - submitButton.setIcon(startIcon); - submitButton.setEnabled(true); - saveButton.setEnabled(sourceCode != null); - updateButtons(prevButton, nextButton); - questionPane.setText(""); - }); } catch (Exception e) { e.printStackTrace(); submitButton.setIcon(startIcon); diff --git a/src/main/java/io/github/jeddict/ai/hints/MethodFix.java b/src/main/java/io/github/jeddict/ai/hints/MethodFix.java index 26b1cbc..db056cf 100644 --- a/src/main/java/io/github/jeddict/ai/hints/MethodFix.java +++ b/src/main/java/io/github/jeddict/ai/hints/MethodFix.java @@ -23,8 +23,9 @@ import com.sun.source.tree.Tree; import static com.sun.source.tree.Tree.Kind.METHOD; import com.sun.source.util.TreePath; +import io.github.jeddict.ai.JeddictUpdateManager; import io.github.jeddict.ai.completion.Action; -import io.github.jeddict.ai.JeddictChatModel; +import io.github.jeddict.ai.lang.JeddictChatModel; import io.github.jeddict.ai.util.SourceUtil; import static io.github.jeddict.ai.util.SourceUtil.geIndentaion; import static io.github.jeddict.ai.util.StringUtil.removeCodeBlockMarkers; @@ -67,11 +68,11 @@ public MethodFix(TreePathHandle tpHandle, String compliationError, String action @Override protected String getText() { if (action == Action.COMPILATION_ERROR) { - return NbBundle.getMessage(JeddictChatModel.class, "HINT_METHOD_COMPILATION_ERROR", actionTitleParam); + return NbBundle.getMessage(JeddictUpdateManager.class, "HINT_METHOD_COMPILATION_ERROR", actionTitleParam); } else if (action == Action.ENHANCE) { - return NbBundle.getMessage(JeddictChatModel.class, "HINT_METHOD_ENHANCE"); + return NbBundle.getMessage(JeddictUpdateManager.class, "HINT_METHOD_ENHANCE"); } else { - return NbBundle.getMessage(JeddictChatModel.class, "HINT_METHOD_QUERY"); + return NbBundle.getMessage(JeddictUpdateManager.class, "HINT_METHOD_QUERY"); } } diff --git a/src/main/java/io/github/jeddict/ai/hints/RestEndpointFix.java b/src/main/java/io/github/jeddict/ai/hints/RestEndpointFix.java index d5ebaea..8023881 100644 --- a/src/main/java/io/github/jeddict/ai/hints/RestEndpointFix.java +++ b/src/main/java/io/github/jeddict/ai/hints/RestEndpointFix.java @@ -23,8 +23,9 @@ import static com.sun.source.tree.Tree.Kind.CLASS; import static com.sun.source.tree.Tree.Kind.INTERFACE; import com.sun.source.util.TreePath; +import io.github.jeddict.ai.JeddictUpdateManager; import io.github.jeddict.ai.completion.Action; -import io.github.jeddict.ai.JeddictChatModel; +import io.github.jeddict.ai.lang.JeddictChatModel; import io.github.jeddict.ai.util.SourceUtil; import static io.github.jeddict.ai.util.StringUtil.removeCodeBlockMarkers; import javax.lang.model.element.Element; @@ -54,7 +55,7 @@ public RestEndpointFix(TreePathHandle tpHandle, Action action, ElementHandle cla @Override protected String getText() { - return NbBundle.getMessage(JeddictChatModel.class, "HINT_REST_ENDPOINT"); + return NbBundle.getMessage(JeddictUpdateManager.class, "HINT_REST_ENDPOINT"); } @Override diff --git a/src/main/java/io/github/jeddict/ai/hints/TextFix.java b/src/main/java/io/github/jeddict/ai/hints/TextFix.java index 7ce8408..09683c9 100644 --- a/src/main/java/io/github/jeddict/ai/hints/TextFix.java +++ b/src/main/java/io/github/jeddict/ai/hints/TextFix.java @@ -22,8 +22,9 @@ import com.sun.source.tree.Tree; import static com.sun.source.tree.Tree.Kind.STRING_LITERAL; import com.sun.source.util.TreePath; +import io.github.jeddict.ai.JeddictUpdateManager; import io.github.jeddict.ai.completion.Action; -import io.github.jeddict.ai.JeddictChatModel; +import io.github.jeddict.ai.lang.JeddictChatModel; import org.netbeans.api.java.source.JavaSource; import org.netbeans.api.java.source.TreePathHandle; import org.netbeans.api.java.source.WorkingCopy; @@ -48,9 +49,9 @@ public TextFix(TreePathHandle tpHandle, Action action, TreePath treePath) { @Override protected String getText() { if (action == Action.ENHANCE) { - return NbBundle.getMessage(JeddictChatModel.class, "HINT_ENHANCE_TEXT"); + return NbBundle.getMessage(JeddictUpdateManager.class, "HINT_ENHANCE_TEXT"); } else { - return NbBundle.getMessage(JeddictChatModel.class, "HINT_FIX_GRAMMAR"); + return NbBundle.getMessage(JeddictUpdateManager.class, "HINT_FIX_GRAMMAR"); } } diff --git a/src/main/java/io/github/jeddict/ai/hints/VariableFix.java b/src/main/java/io/github/jeddict/ai/hints/VariableFix.java index 1846ad8..0ccf059 100644 --- a/src/main/java/io/github/jeddict/ai/hints/VariableFix.java +++ b/src/main/java/io/github/jeddict/ai/hints/VariableFix.java @@ -20,8 +20,9 @@ import com.sun.source.tree.Tree; import com.sun.source.util.TreePath; +import io.github.jeddict.ai.JeddictUpdateManager; import io.github.jeddict.ai.completion.Action; -import io.github.jeddict.ai.JeddictChatModel; +import io.github.jeddict.ai.lang.JeddictChatModel; import io.github.jeddict.ai.util.SourceUtil; import static io.github.jeddict.ai.util.StringUtil.removeCodeBlockMarkers; import javax.lang.model.element.Element; @@ -60,7 +61,7 @@ public VariableFix(TreePathHandle tpHandle, String compliationError, String acti @Override protected String getText() { - return NbBundle.getMessage(JeddictChatModel.class, "HINT_VARIABLE_COMPILATION_ERROR", actionTitleParam); + return NbBundle.getMessage(JeddictUpdateManager.class, "HINT_VARIABLE_COMPILATION_ERROR", actionTitleParam); } @Override diff --git a/src/main/java/io/github/jeddict/ai/hints/VariableNameFix.java b/src/main/java/io/github/jeddict/ai/hints/VariableNameFix.java index 9b07afd..655bf45 100644 --- a/src/main/java/io/github/jeddict/ai/hints/VariableNameFix.java +++ b/src/main/java/io/github/jeddict/ai/hints/VariableNameFix.java @@ -28,8 +28,9 @@ import com.sun.source.tree.VariableTree; import com.sun.source.util.TreePath; import com.sun.source.util.TreeScanner; +import io.github.jeddict.ai.JeddictUpdateManager; import io.github.jeddict.ai.completion.Action; -import io.github.jeddict.ai.JeddictChatModel; +import io.github.jeddict.ai.lang.JeddictChatModel; import java.util.List; import java.util.stream.Collectors; import javax.lang.model.element.Element; @@ -59,7 +60,7 @@ public VariableNameFix(TreePathHandle tpHandle, Action action, TreePath treePath @Override protected String getText() { - return NbBundle.getMessage(JeddictChatModel.class, "HINT_VARIABLE_NAME_ENHANCE"); + return NbBundle.getMessage(JeddictUpdateManager.class, "HINT_VARIABLE_NAME_ENHANCE"); } @Override diff --git a/src/main/java/io/github/jeddict/ai/JeddictChatModel.java b/src/main/java/io/github/jeddict/ai/lang/JeddictChatModel.java similarity index 89% rename from src/main/java/io/github/jeddict/ai/JeddictChatModel.java rename to src/main/java/io/github/jeddict/ai/lang/JeddictChatModel.java index 6c2020f..cc380c9 100644 --- a/src/main/java/io/github/jeddict/ai/JeddictChatModel.java +++ b/src/main/java/io/github/jeddict/ai/lang/JeddictChatModel.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package io.github.jeddict.ai; +package io.github.jeddict.ai.lang; /** * @@ -24,18 +24,31 @@ */ import com.sun.source.tree.Tree; import com.sun.source.util.TreePath; +import dev.langchain4j.model.StreamingResponseHandler; import dev.langchain4j.model.anthropic.AnthropicChatModel; +import dev.langchain4j.model.anthropic.AnthropicStreamingChatModel; import dev.langchain4j.model.chat.ChatLanguageModel; +import dev.langchain4j.model.chat.StreamingChatLanguageModel; import dev.langchain4j.model.googleai.GoogleAiGeminiChatModel; import dev.langchain4j.model.localai.LocalAiChatModel; +import dev.langchain4j.model.localai.LocalAiStreamingChatModel; import dev.langchain4j.model.mistralai.MistralAiChatModel; +import dev.langchain4j.model.mistralai.MistralAiStreamingChatModel; import dev.langchain4j.model.ollama.OllamaChatModel; +import dev.langchain4j.model.ollama.OllamaStreamingChatModel; import dev.langchain4j.model.openai.OpenAiChatModel; -import static io.github.jeddict.ai.settings.GenAIProvider.ANTHROPIC; -import static io.github.jeddict.ai.settings.GenAIProvider.OLLAMA; +import dev.langchain4j.model.openai.OpenAiStreamingChatModel; import io.github.jeddict.ai.models.LMStudioChatModel; +import static io.github.jeddict.ai.settings.GenAIProvider.ANTHROPIC; +import static io.github.jeddict.ai.settings.GenAIProvider.CUSTOM_OPEN_AI; import static io.github.jeddict.ai.settings.GenAIProvider.DEEPINFRA; +import static io.github.jeddict.ai.settings.GenAIProvider.DEEPSEEK; +import static io.github.jeddict.ai.settings.GenAIProvider.GOOGLE; +import static io.github.jeddict.ai.settings.GenAIProvider.GPT4ALL; +import static io.github.jeddict.ai.settings.GenAIProvider.GROQ; import static io.github.jeddict.ai.settings.GenAIProvider.LM_STUDIO; +import static io.github.jeddict.ai.settings.GenAIProvider.MISTRAL; +import static io.github.jeddict.ai.settings.GenAIProvider.OLLAMA; import static io.github.jeddict.ai.settings.GenAIProvider.OPEN_AI; import io.github.jeddict.ai.settings.PreferencesManager; import static io.github.jeddict.ai.util.MimeUtil.MIME_TYPE_DESCRIPTIONS; @@ -58,66 +71,123 @@ public class JeddictChatModel { private ChatLanguageModel model; + private StreamingChatLanguageModel streamModel; private int cachedClassDatasLength = -1; // Cache the length of classDatas - PreferencesManager preferencesManager = PreferencesManager.getInstance(); - + private PreferencesManager preferencesManager = PreferencesManager.getInstance(); + private StreamingResponseHandler handler; + public JeddictChatModel() { + this(null); + } + + public JeddictChatModel(StreamingResponseHandler handler) { + this.handler = handler; if (null != preferencesManager.getModel()) { - switch (preferencesManager.getProvider()) { - case GOOGLE -> - model = GoogleAiGeminiChatModel.builder() - .apiKey(preferencesManager.getApiKey()) - .modelName(preferencesManager.getModelName()) - .build(); - case OPEN_AI -> - model = OpenAiChatModel.builder() - .apiKey(preferencesManager.getApiKey()) - .modelName(preferencesManager.getModelName()) - .build(); - case DEEPINFRA, DEEPSEEK, GROQ, CUSTOM_OPEN_AI -> - model = OpenAiChatModel.builder() - .baseUrl(preferencesManager.getProviderLocation()) - .apiKey(preferencesManager.getApiKey()) - .modelName(preferencesManager.getModelName()) - .build(); - case MISTRAL -> - model = MistralAiChatModel.builder() - .apiKey(preferencesManager.getApiKey()) - .modelName(preferencesManager.getModelName()) - .build(); - case ANTHROPIC -> - model = AnthropicChatModel.builder() - .apiKey(preferencesManager.getApiKey()) - .modelName(preferencesManager.getModelName()) - .build(); - case OLLAMA -> - model = OllamaChatModel.builder() - .baseUrl(preferencesManager.getProviderLocation()) - .modelName(preferencesManager.getModelName()) - .build(); - case LM_STUDIO -> - model = LMStudioChatModel.builder() - .baseUrl(preferencesManager.getProviderLocation()) - .modelName(preferencesManager.getModelName()) - .build(); - case GPT4ALL -> - model = LocalAiChatModel.builder() - .baseUrl(preferencesManager.getProviderLocation()) - .modelName(preferencesManager.getModelName()) - .build(); + if (preferencesManager.isStreamEnabled() && handler != null) { + switch (preferencesManager.getProvider()) { + case GOOGLE -> + model = GoogleAiGeminiChatModel.builder() + .apiKey(preferencesManager.getApiKey()) + .modelName(preferencesManager.getModelName()) + .build(); + case OPEN_AI -> + streamModel = OpenAiStreamingChatModel.builder() + .apiKey(preferencesManager.getApiKey()) + .modelName(preferencesManager.getModelName()) + .build(); + case DEEPINFRA, DEEPSEEK, GROQ, CUSTOM_OPEN_AI -> + streamModel = OpenAiStreamingChatModel.builder() + .baseUrl(preferencesManager.getProviderLocation()) + .apiKey(preferencesManager.getApiKey()) + .modelName(preferencesManager.getModelName()) + .build(); + case MISTRAL -> + streamModel = MistralAiStreamingChatModel.builder() + .apiKey(preferencesManager.getApiKey()) + .modelName(preferencesManager.getModelName()) + .build(); + case ANTHROPIC -> + streamModel = AnthropicStreamingChatModel.builder() + .apiKey(preferencesManager.getApiKey()) + .modelName(preferencesManager.getModelName()) + .build(); + case OLLAMA -> + streamModel = OllamaStreamingChatModel.builder() + .baseUrl(preferencesManager.getProviderLocation()) + .modelName(preferencesManager.getModelName()) + .build(); + case LM_STUDIO -> + model = LMStudioChatModel.builder() + .baseUrl(preferencesManager.getProviderLocation()) + .modelName(preferencesManager.getModelName()) + .build(); + case GPT4ALL -> + streamModel = LocalAiStreamingChatModel.builder() + .baseUrl(preferencesManager.getProviderLocation()) + .modelName(preferencesManager.getModelName()) + .build(); + } + } else { + switch (preferencesManager.getProvider()) { + case GOOGLE -> + model = GoogleAiGeminiChatModel.builder() + .apiKey(preferencesManager.getApiKey()) + .modelName(preferencesManager.getModelName()) + .build(); + case OPEN_AI -> + model = OpenAiChatModel.builder() + .apiKey(preferencesManager.getApiKey()) + .modelName(preferencesManager.getModelName()) + .build(); + case DEEPINFRA, DEEPSEEK, GROQ, CUSTOM_OPEN_AI -> + model = OpenAiChatModel.builder() + .baseUrl(preferencesManager.getProviderLocation()) + .apiKey(preferencesManager.getApiKey()) + .modelName(preferencesManager.getModelName()) + .build(); + case MISTRAL -> + model = MistralAiChatModel.builder() + .apiKey(preferencesManager.getApiKey()) + .modelName(preferencesManager.getModelName()) + .build(); + case ANTHROPIC -> + model = AnthropicChatModel.builder() + .apiKey(preferencesManager.getApiKey()) + .modelName(preferencesManager.getModelName()) + .build(); + case OLLAMA -> + model = OllamaChatModel.builder() + .baseUrl(preferencesManager.getProviderLocation()) + .modelName(preferencesManager.getModelName()) + .build(); + case LM_STUDIO -> + model = LMStudioChatModel.builder() + .baseUrl(preferencesManager.getProviderLocation()) + .modelName(preferencesManager.getModelName()) + .build(); + case GPT4ALL -> + model = LocalAiChatModel.builder() + .baseUrl(preferencesManager.getProviderLocation()) + .modelName(preferencesManager.getModelName()) + .build(); + } } } } private String generate(String prompt) { - if (model == null) { + if (model == null && handler == null) { JOptionPane.showMessageDialog(null, "AI assistance model not intitalized.", "Error in AI Assistance", JOptionPane.ERROR_MESSAGE); } try { - return model.generate(prompt); + if (streamModel != null) { + streamModel.generate(prompt, handler); + } else { + return model.generate(prompt); + } } catch (Exception e) { String errorMessage = e.getMessage(); if (e.getCause() != null @@ -974,7 +1044,7 @@ public String generateTestCase( promptExtend.append("Java Class Content:\n").append(classContent).append("\n\n") .append("Generate ").append(testCaseType).append(" test cases for this class. Include assertions and necessary mock setups. "); } - + promptBuilder.append("You are an API server that provides "); String rules = preferencesManager.getCommonPromptRules(); if (rules != null && !rules.isEmpty()) { diff --git a/src/main/java/io/github/jeddict/ai/lang/JeddictStreamHandler.java b/src/main/java/io/github/jeddict/ai/lang/JeddictStreamHandler.java new file mode 100644 index 0000000..697cd2e --- /dev/null +++ b/src/main/java/io/github/jeddict/ai/lang/JeddictStreamHandler.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.github.jeddict.ai.lang; + +import dev.langchain4j.data.message.AiMessage; +import dev.langchain4j.model.StreamingResponseHandler; +import dev.langchain4j.model.output.Response; +import io.github.jeddict.ai.components.AssistantTopComponent; +import static io.github.jeddict.ai.util.StringUtil.removeCodeBlockMarkers; +import javax.swing.JEditorPane; +import javax.swing.SwingUtilities; + +/** + * + * @author Shiwani Gupta + */ +public abstract class JeddictStreamHandler implements StreamingResponseHandler { + + private final AssistantTopComponent topComponent; + private boolean init = true; + private JEditorPane editorPane; + + public JeddictStreamHandler(AssistantTopComponent topComponent) { + this.topComponent = topComponent; + } + + @Override + public void onNext(String string) { + if (init) { + topComponent.clear(); + editorPane = topComponent.createPane(); + editorPane.setText(string); + init = false; + } else { + String partial = editorPane.getText() + string; + editorPane.setText(partial); + } + } + + @Override + public void onError(Throwable thrwbl) { + } + + @Override + public void onComplete(Response out) { + SwingUtilities.invokeLater(() -> { + String response = out.content().text(); + if (response != null && !response.isEmpty()) { + response = removeCodeBlockMarkers(response); + onComplete(response); + } + }); + } + + public abstract void onComplete(String response); +} diff --git a/src/main/java/io/github/jeddict/ai/Snippet.java b/src/main/java/io/github/jeddict/ai/lang/Snippet.java similarity index 90% rename from src/main/java/io/github/jeddict/ai/Snippet.java rename to src/main/java/io/github/jeddict/ai/lang/Snippet.java index ea8f8f0..139aa89 100644 --- a/src/main/java/io/github/jeddict/ai/Snippet.java +++ b/src/main/java/io/github/jeddict/ai/lang/Snippet.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package io.github.jeddict.ai; +package io.github.jeddict.ai.lang; import java.util.ArrayList; import java.util.List; @@ -27,11 +27,11 @@ */ public class Snippet { - List imports = new ArrayList<>(); + private List imports = new ArrayList<>(); - String snippet; + private final String snippet; - String description; + private String description; public Snippet(String snippet, String description, List imports) { this.snippet = snippet; diff --git a/src/main/java/io/github/jeddict/ai/scanner/ProjectClassScanner.java b/src/main/java/io/github/jeddict/ai/scanner/ProjectClassScanner.java index 03559c8..df56a5e 100644 --- a/src/main/java/io/github/jeddict/ai/scanner/ProjectClassScanner.java +++ b/src/main/java/io/github/jeddict/ai/scanner/ProjectClassScanner.java @@ -23,7 +23,7 @@ import com.sun.source.tree.ModifiersTree; import com.sun.source.tree.Tree; import com.sun.source.tree.VariableTree; -import io.github.jeddict.ai.JeddictChatModel; +import io.github.jeddict.ai.lang.JeddictChatModel; import io.github.jeddict.ai.settings.AIClassContext; import org.netbeans.api.java.source.JavaSource; import org.netbeans.api.project.Project; diff --git a/src/main/java/io/github/jeddict/ai/settings/AIAssistancePanel.form b/src/main/java/io/github/jeddict/ai/settings/AIAssistancePanel.form index 0c7db09..a958548 100644 --- a/src/main/java/io/github/jeddict/ai/settings/AIAssistancePanel.form +++ b/src/main/java/io/github/jeddict/ai/settings/AIAssistancePanel.form @@ -37,17 +37,14 @@ - - - - + - - + + @@ -298,27 +295,6 @@ - - - - - - - - - - - - - - - - - - - - - @@ -326,7 +302,7 @@ - + @@ -351,7 +327,7 @@ - + @@ -368,7 +344,7 @@ - + @@ -444,6 +420,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/io/github/jeddict/ai/settings/AIAssistancePanel.java b/src/main/java/io/github/jeddict/ai/settings/AIAssistancePanel.java index ceaadb2..325b6b0 100644 --- a/src/main/java/io/github/jeddict/ai/settings/AIAssistancePanel.java +++ b/src/main/java/io/github/jeddict/ai/settings/AIAssistancePanel.java @@ -86,8 +86,6 @@ private void initComponents() { aiAssistantActivationCheckBox = new javax.swing.JCheckBox(); enableHintCheckBox = new javax.swing.JCheckBox(); enableSmartCodeCheckBox = new javax.swing.JCheckBox(); - excludeJavadocCommentsPane = new javax.swing.JLayeredPane(); - excludeJavadocCommentsCheckBox = new javax.swing.JCheckBox(); askAIPane = new javax.swing.JLayeredPane(); fileFilterationPane = new javax.swing.JLayeredPane(); fileExtLabel = new javax.swing.JLabel(); @@ -97,6 +95,9 @@ private void initComponents() { jLayeredPane2 = new javax.swing.JLayeredPane(); jScrollPane2 = new javax.swing.JScrollPane(); excludeDirTable = new javax.swing.JTable(); + jLayeredPane1 = new javax.swing.JLayeredPane(); + enableStreamCheckbox = new javax.swing.JCheckBox(); + excludeJavadocCommentsCheckBox = new javax.swing.JCheckBox(); inlineCompletionPane = new javax.swing.JLayeredPane(); classContextPane = new javax.swing.JLayeredPane(); classContextLabelPane = new javax.swing.JLayeredPane(); @@ -235,19 +236,6 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { activationParentPane.add(activationPane); - excludeJavadocCommentsPane.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT)); - - org.openide.awt.Mnemonics.setLocalizedText(excludeJavadocCommentsCheckBox, org.openide.util.NbBundle.getMessage(AIAssistancePanel.class, "AIAssistancePanel.excludeJavadocCommentsCheckBox.text")); // NOI18N - excludeJavadocCommentsCheckBox.setToolTipText(org.openide.util.NbBundle.getMessage(AIAssistancePanel.class, "AIAssistancePanel.excludeJavadocCommentsCheckBox.toolTipText")); // NOI18N - excludeJavadocCommentsCheckBox.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - excludeJavadocCommentsCheckBoxActionPerformed(evt); - } - }); - excludeJavadocCommentsPane.add(excludeJavadocCommentsCheckBox); - - activationParentPane.add(excludeJavadocCommentsPane); - providersPane.add(activationParentPane); jTabbedPane1.addTab(org.openide.util.NbBundle.getMessage(AIAssistancePanel.class, "AIAssistancePanel.providersPane.TabConstraints.tabTitle"), providersPane); // NOI18N @@ -279,7 +267,7 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { fileFilterationPane.setLayout(fileFilterationPaneLayout); fileFilterationPaneLayout.setHorizontalGroup( fileFilterationPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jScrollPane3, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 543, Short.MAX_VALUE) + .addComponent(jScrollPane3, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 558, Short.MAX_VALUE) .addGroup(fileFilterationPaneLayout.createSequentialGroup() .addContainerGap() .addComponent(fileExtLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 201, javax.swing.GroupLayout.PREFERRED_SIZE) @@ -293,7 +281,7 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { .addGroup(fileFilterationPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(fileExtLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jLabel1)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 7, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(jScrollPane3, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE) .addContainerGap()) ); @@ -309,6 +297,23 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { askAIPane.add(jLayeredPane2); + jLayeredPane1.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT, 15, 5)); + + org.openide.awt.Mnemonics.setLocalizedText(enableStreamCheckbox, org.openide.util.NbBundle.getMessage(AIAssistancePanel.class, "AIAssistancePanel.enableStreamCheckbox.text")); // NOI18N + enableStreamCheckbox.setToolTipText(org.openide.util.NbBundle.getMessage(AIAssistancePanel.class, "AIAssistancePanel.enableStreamCheckbox.toolTipText")); // NOI18N + jLayeredPane1.add(enableStreamCheckbox); + + org.openide.awt.Mnemonics.setLocalizedText(excludeJavadocCommentsCheckBox, org.openide.util.NbBundle.getMessage(AIAssistancePanel.class, "AIAssistancePanel.excludeJavadocCommentsCheckBox.text")); // NOI18N + excludeJavadocCommentsCheckBox.setToolTipText(org.openide.util.NbBundle.getMessage(AIAssistancePanel.class, "AIAssistancePanel.excludeJavadocCommentsCheckBox.toolTipText")); // NOI18N + excludeJavadocCommentsCheckBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + excludeJavadocCommentsCheckBoxActionPerformed(evt); + } + }); + jLayeredPane1.add(excludeJavadocCommentsCheckBox); + + askAIPane.add(jLayeredPane1); + jTabbedPane1.addTab(org.openide.util.NbBundle.getMessage(AIAssistancePanel.class, "AIAssistancePanel.askAIPane.TabConstraints.tabTitle"), askAIPane); // NOI18N inlineCompletionPane.setLayout(new java.awt.GridLayout(4, 1)); @@ -420,18 +425,32 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(jTabbedPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 543, Short.MAX_VALUE) - .addContainerGap()) + .addComponent(jTabbedPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 558, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addComponent(jTabbedPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 344, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(80, Short.MAX_VALUE)) + .addComponent(jTabbedPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 329, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 95, Short.MAX_VALUE)) ); }// //GEN-END:initComponents + private void cleanDataButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cleanDataButtonActionPerformed + ProjectClassScanner.clear(); + JOptionPane.showMessageDialog(this, "Cache has been cleared successfully!", "Information", JOptionPane.INFORMATION_MESSAGE); + }//GEN-LAST:event_cleanDataButtonActionPerformed + + private void showDescriptionCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showDescriptionCheckBoxActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_showDescriptionCheckBoxActionPerformed + + private void varContextComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_varContextComboBoxActionPerformed + AIClassContext selectedContext = (AIClassContext) varContextComboBox.getSelectedItem(); + if (selectedContext != null) { + varContextHelp.setText(selectedContext.getDescription()); + } + }//GEN-LAST:event_varContextComboBoxActionPerformed + private void classContextComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_classContextComboBoxActionPerformed AIClassContext selectedContext = (AIClassContext) classContextComboBox.getSelectedItem(); if (selectedContext != null) { @@ -439,6 +458,14 @@ private void classContextComboBoxActionPerformed(java.awt.event.ActionEvent evt) } }//GEN-LAST:event_classContextComboBoxActionPerformed + private void excludeJavadocCommentsCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_excludeJavadocCommentsCheckBoxActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_excludeJavadocCommentsCheckBoxActionPerformed + + private void enableHintCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_enableHintCheckBoxActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_enableHintCheckBoxActionPerformed + private void aiAssistantActivationCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_aiAssistantActivationCheckBoxActionPerformed // if unchecked then disable the hint and smart checkbox if (!aiAssistantActivationCheckBox.isSelected()) { @@ -450,10 +477,6 @@ private void aiAssistantActivationCheckBoxActionPerformed(java.awt.event.ActionE } }//GEN-LAST:event_aiAssistantActivationCheckBoxActionPerformed - private void enableHintCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_enableHintCheckBoxActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_enableHintCheckBoxActionPerformed - private void modelComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_modelComboBoxActionPerformed String selectedContext = (String) modelComboBox.getSelectedItem(); if (selectedContext != null && getModel(selectedContext) != null) { @@ -463,21 +486,19 @@ private void modelComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN } }//GEN-LAST:event_modelComboBoxActionPerformed - private void cleanDataButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cleanDataButtonActionPerformed - ProjectClassScanner.clear(); - JOptionPane.showMessageDialog(this, "Cache has been cleared successfully!", "Information", JOptionPane.INFORMATION_MESSAGE); - }//GEN-LAST:event_cleanDataButtonActionPerformed - - private void showDescriptionCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showDescriptionCheckBoxActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_showDescriptionCheckBoxActionPerformed + private void apiKeyFieldFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_apiKeyFieldFocusLost + GenAIProvider selectedProvider = (GenAIProvider) providerComboBox.getSelectedItem(); + if (selectedProvider != null) { + updateModelComboBox(selectedProvider); + } + }//GEN-LAST:event_apiKeyFieldFocusLost private void providerComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_providerComboBoxActionPerformed GenAIProvider selectedProvider = (GenAIProvider) providerComboBox.getSelectedItem(); if (selectedProvider == GenAIProvider.DEEPINFRA - || selectedProvider == GenAIProvider.DEEPSEEK - || selectedProvider == GenAIProvider.GROQ - || selectedProvider == GenAIProvider.CUSTOM_OPEN_AI) { + || selectedProvider == GenAIProvider.DEEPSEEK + || selectedProvider == GenAIProvider.GROQ + || selectedProvider == GenAIProvider.CUSTOM_OPEN_AI) { apiKeyLabel.setVisible(true); apiKeyField.setVisible(true); apiKeyPane.setVisible(true); @@ -494,9 +515,9 @@ private void providerComboBoxActionPerformed(java.awt.event.ActionEvent evt) {// providerLocationField.setVisible(true); providerLocationPane.setVisible(true); } else if (selectedProvider == GenAIProvider.GOOGLE - || selectedProvider == GenAIProvider.OPEN_AI - || selectedProvider == GenAIProvider.MISTRAL - || selectedProvider == GenAIProvider.ANTHROPIC) { + || selectedProvider == GenAIProvider.OPEN_AI + || selectedProvider == GenAIProvider.MISTRAL + || selectedProvider == GenAIProvider.ANTHROPIC) { apiKeyLabel.setVisible(true); apiKeyField.setVisible(true); apiKeyPane.setVisible(true); @@ -515,18 +536,18 @@ private void providerComboBoxActionPerformed(java.awt.event.ActionEvent evt) {// if (null != selectedProvider) { switch (selectedProvider) { case OLLAMA -> - providerLocationField.setText(new OllamaModelFetcher().getAPIUrl()); + providerLocationField.setText(new OllamaModelFetcher().getAPIUrl()); case LM_STUDIO -> - providerLocationField.setText(new LMStudioModelFetcher().getAPIUrl()); + providerLocationField.setText(new LMStudioModelFetcher().getAPIUrl()); case GPT4ALL -> - providerLocationField.setText(new GPT4AllModelFetcher().getAPIUrl()); + providerLocationField.setText(new GPT4AllModelFetcher().getAPIUrl()); } } } apiKeyField.setText(preferencesManager.getApiKey((GenAIProvider) providerComboBox.getSelectedItem())); if (apiKeyLabel.isVisible() - && selectedProvider != null - && !selectedProvider.getApiKeyUrl().isEmpty()) { + && selectedProvider != null + && !selectedProvider.getApiKeyUrl().isEmpty()) { String apiKeyUrl = selectedProvider.getApiKeyUrl(); apiKeyInfo.setText("" + apiKeyUrl + ""); apiKeyInfo.setCursor(new Cursor(Cursor.HAND_CURSOR)); @@ -552,7 +573,7 @@ public void mouseExited(MouseEvent e) { }); } if (selectedProvider != null - && !selectedProvider.getModelInfoUrl().isEmpty()) { + && !selectedProvider.getModelInfoUrl().isEmpty()) { String modelInfoUrl = selectedProvider.getModelInfoUrl(); modelsInfo.setText("" + modelInfoUrl + ""); modelsInfo.setCursor(new Cursor(Cursor.HAND_CURSOR)); @@ -582,24 +603,6 @@ public void mouseExited(MouseEvent e) { } }//GEN-LAST:event_providerComboBoxActionPerformed - private void apiKeyFieldFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_apiKeyFieldFocusLost - GenAIProvider selectedProvider = (GenAIProvider) providerComboBox.getSelectedItem(); - if (selectedProvider != null) { - updateModelComboBox(selectedProvider); - } - }//GEN-LAST:event_apiKeyFieldFocusLost - - private void excludeJavadocCommentsCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_excludeJavadocCommentsCheckBoxActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_excludeJavadocCommentsCheckBoxActionPerformed - - private void varContextComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_varContextComboBoxActionPerformed - AIClassContext selectedContext = (AIClassContext) varContextComboBox.getSelectedItem(); - if (selectedContext != null) { - varContextHelp.setText(selectedContext.getDescription()); - } - }//GEN-LAST:event_varContextComboBoxActionPerformed - private void updateModelComboBox(GenAIProvider selectedProvider) { modelComboBox.removeAllItems(); for (String model : getModelList(selectedProvider)) { @@ -659,6 +662,7 @@ void load() { modelComboBox.setSelectedItem(preferencesManager.getModel()); showDescriptionCheckBox.setSelected(preferencesManager.isDescriptionEnabled()); fileExtField.setText(preferencesManager.getFileExtensionToInclude()); + enableStreamCheckbox.setSelected(preferencesManager.isStreamEnabled()); excludeJavadocCommentsCheckBox.setSelected(preferencesManager.isExcludeJavadocEnabled()); testPromptField.setText(preferencesManager.getTestCasePrompt()); @@ -692,6 +696,7 @@ void store() { preferencesManager.setDescriptionEnabled(showDescriptionCheckBox.isSelected()); preferencesManager.setFileExtensionToInclude(fileExtField.getText()); preferencesManager.setExcludeDirs(getCommaSeparatedValues(excludeTableModel)); + preferencesManager.setStreamEnabled(enableStreamCheckbox.isSelected()); preferencesManager.setExcludeJavadocEnabled(excludeJavadocCommentsCheckBox.isSelected()); preferencesManager.setTestCasePrompt(testPromptField.getText()); @@ -806,9 +811,9 @@ boolean valid() { private javax.swing.JButton cleanDataButton; private javax.swing.JCheckBox enableHintCheckBox; private javax.swing.JCheckBox enableSmartCodeCheckBox; + private javax.swing.JCheckBox enableStreamCheckbox; private javax.swing.JTable excludeDirTable; private javax.swing.JCheckBox excludeJavadocCommentsCheckBox; - private javax.swing.JLayeredPane excludeJavadocCommentsPane; private javax.swing.JTextArea fileExtField; private javax.swing.JLabel fileExtLabel; private javax.swing.JLayeredPane fileFilterationPane; @@ -817,6 +822,7 @@ boolean valid() { private javax.swing.JLayeredPane hintPane; private javax.swing.JLayeredPane inlineCompletionPane; private javax.swing.JLabel jLabel1; + private javax.swing.JLayeredPane jLayeredPane1; private javax.swing.JLayeredPane jLayeredPane2; private javax.swing.JScrollPane jScrollPane2; private javax.swing.JScrollPane jScrollPane3; diff --git a/src/main/java/io/github/jeddict/ai/settings/PreferencesManager.java b/src/main/java/io/github/jeddict/ai/settings/PreferencesManager.java index 7f2ec05..de02ca2 100644 --- a/src/main/java/io/github/jeddict/ai/settings/PreferencesManager.java +++ b/src/main/java/io/github/jeddict/ai/settings/PreferencesManager.java @@ -41,6 +41,8 @@ public class PreferencesManager { private static final String PROVIDER_LOCATION_PREFERENCES = "provider_location"; private static final String PROVIDER_PREFERENCE = "provider"; private static final String MODEL_PREFERENCE = "model"; + private static final String COMMON_PROMPT_RULES_PREFERENCE = "commonPromptRules"; + private static final String ENABLE_STREAM_PREFERENCE = "enableStream"; private final List EXCLUDE_DIR_DEFAULT = Arrays.asList( // Test Resources @@ -77,7 +79,6 @@ public class PreferencesManager { "trace", "cache", "backup", - // Other Configuration/Files ".env", // Environment variable definitions "docker-compose.yml", // Docker Compose config @@ -100,14 +101,12 @@ public class PreferencesManager { "CODE_OF_CONDUCT.md", "CONTRIBUTING.md", "LICENSE", - // Security and Configuration Files "secrets", // Directory for secrets "credentials", // Directory for credentials "private", "confidential", "vault", - // CI configuration ".gitlab-ci.yml", ".travis.yml", @@ -129,17 +128,17 @@ public static PreferencesManager getInstance() { } public void clearApiKey() { - preferences.remove(getProvider().name()+ API_KEY_PREFERENCES); + preferences.remove(getProvider().name() + API_KEY_PREFERENCES); } public void setApiKey(String key) { - preferences.put(getProvider().name()+API_KEY_PREFERENCES, key); + preferences.put(getProvider().name() + API_KEY_PREFERENCES, key); } - + public String getApiKey() { return getApiKey(false); } - + String getApiKey(GenAIProvider provider) { return preferences.get(provider.name() + API_KEY_PREFERENCES, null); } @@ -153,7 +152,7 @@ public String getApiKey(boolean headless) { } if (apiKey == null || apiKey.isEmpty()) { // If not found in environment or system properties, check Preferences - apiKey = preferences.get(getProvider().name() +API_KEY_PREFERENCES, null); + apiKey = preferences.get(getProvider().name() + API_KEY_PREFERENCES, null); } if (apiKey == null || apiKey.isEmpty()) { @@ -359,11 +358,9 @@ public void setTestCasePrompt(String prompt) { preferences.put("testCasePrompt", prompt); } } - - private static final String COMMON_PROMPT_RULES_PREFERENCE = "commonPromptRules"; public String getCommonPromptRules() { - return preferences.get(COMMON_PROMPT_RULES_PREFERENCE, "Default rules for prompts."); + return preferences.get(COMMON_PROMPT_RULES_PREFERENCE, ""); } public void setCommonPromptRules(String rules) { @@ -372,4 +369,12 @@ public void setCommonPromptRules(String rules) { } } + public boolean isStreamEnabled() { + return preferences.getBoolean(ENABLE_STREAM_PREFERENCE, false); // Default to false + } + + public void setStreamEnabled(boolean enabled) { + preferences.putBoolean(ENABLE_STREAM_PREFERENCE, enabled); + } + } diff --git a/src/main/java/io/github/jeddict/ai/util/EditorUtil.java b/src/main/java/io/github/jeddict/ai/util/EditorUtil.java index bae9816..4c1520e 100644 --- a/src/main/java/io/github/jeddict/ai/util/EditorUtil.java +++ b/src/main/java/io/github/jeddict/ai/util/EditorUtil.java @@ -63,15 +63,14 @@ public static String updateEditors(AssistantTopComponent topComponent, String te code.append('\n').append(codeContent).append('\n'); editorPane = topComponent.createCodePane(getMimeType(codeType), codeContent); } else { - String html = renderer.render(parser.parse(parts[i].trim())); - editorPane = topComponent.createHtmlPane(html); + editorPane = topComponent.createCodePane(getMimeType(null), parts[i]); } } } - if (editorPane != null) { - editorPane.setCaretPosition(0); - } +// if (editorPane != null) { +// editorPane.setCaretPosition(0); +// } return code.toString(); } @@ -220,6 +219,9 @@ private static String breakLongLine(String line, int maxLineLength) { // Method to get the NetBeans MIME type for a given ChatGPT code block type public static String getMimeType(String chatGptType) { + if (chatGptType == null) { + return "text/plain"; + } return OPENAI_NETBEANS_EDITOR_MAP.getOrDefault(chatGptType, "text/plain"); // Default to binary if not found } diff --git a/src/main/resources/io/github/jeddict/ai/settings/Bundle.properties b/src/main/resources/io/github/jeddict/ai/settings/Bundle.properties index f2e3b21..4770c69 100644 --- a/src/main/resources/io/github/jeddict/ai/settings/Bundle.properties +++ b/src/main/resources/io/github/jeddict/ai/settings/Bundle.properties @@ -1,38 +1,40 @@ +AIAssistancePanel.excludeDir.text=Directories and Files to Exclude from Context (This setting is applicable at the Project Level) +AIAssistancePanel.testPromptField.text=Type the prompt for generating test cases based on your class context or project. +AIAssistancePanel.testPromptLabel.text=Generate Test Case Prompt: +AIAssistancePanel.hintPane.TabConstraints.tabTitle=Hints +AIAssistancePanel.cleanDataButton.text=Clear Cache +AIAssistancePanel.showDescriptionCheckBox.toolTipText=Show a description along with code snippets for improved understanding of AI-generated suggestions. Helps to explain the reasoning behind the suggested code. +AIAssistancePanel.showDescriptionCheckBox.text=Show Snippet with Description +AIAssistancePanel.varContextComboBox.toolTipText=Select the classes to share with the AI assistant for enhanced context and more accurate suggestions. +AIAssistancePanel.varContextHelp.text=Provide context for the assistant to suggest better variable names, method names, and strings based on your code structure. +AIAssistancePanel.varContextLabel.text=Class Context Scope for Variable Name, Method Name, String Literals: AIAssistancePanel.classContextComboBox.toolTipText=Select the classes to share with the AI assistant for enhanced context and more accurate suggestions. -AIAssistancePanel.aiAssistantActivationCheckBox.text=Activate AI Assistant AIAssistancePanel.classContextHelp.text=Sharing your class context enables the AI assistant to analyze and understand your codebase better. AIAssistancePanel.classContextLabel.text=Class Context Scope (Default): -AIAssistancePanel.cleanDataButton.text=Clear Cache -AIAssistancePanel.gptModelHelp.text=Choose a model based on your performance and accuracy requirements. -AIAssistancePanel.gptModelLabel.text=Model: -AIAssistancePanel.enableHintCheckBox.text=Enable Hints -AIAssistancePanel.aiAssistantActivationCheckBox.toolTipText=Turn the AI assistant on or off to enable smart code completion and suggestions for a better coding experience. -AIAssistancePanel.enableHintCheckBox.toolTipText=Activate hints and suggestions while you work to receive guidance in real time. +AIAssistancePanel.inlineCompletionPane.TabConstraints.tabTitle=Inline Completion +AIAssistancePanel.jLabel1.text=(This setting applies at both Project and Folder Levels) +AIAssistancePanel.fileExtField.text=e.g., java, xml, yaml +AIAssistancePanel.fileExtLabel.text=File Extensions to Include in Context: +AIAssistancePanel.askAIPane.TabConstraints.tabTitle=Chat +AIAssistancePanel.excludeJavadocCommentsCheckBox.toolTipText=Exclude Javadoc comments when generating context to focus solely on code and reduce token usage for better performance. Helps improve accuracy and avoid irrelevant suggestions. +AIAssistancePanel.excludeJavadocCommentsCheckBox.text=Exclude Javadoc Comments in Context AIAssistancePanel.enableSmartCodeCheckBox.toolTipText=Enable inline AI-powered smart code suggestions for a smoother and faster coding experience. AIAssistancePanel.enableSmartCodeCheckBox.text=Enable Inline Completion -AIAssistancePanel.showDescriptionCheckBox.toolTipText=Show a description along with code snippets for improved understanding of AI-generated suggestions. Helps to explain the reasoning behind the suggested code. -AIAssistancePanel.showDescriptionCheckBox.text=Show Snippet with Description -AIAssistancePanel.providerComboBox.toolTipText=Select the Provider for AI assistant. Providers can offer different types of models and capabilities, such as speed, context size, and accuracy. +AIAssistancePanel.enableHintCheckBox.toolTipText=Activate hints and suggestions while you work to receive guidance in real time. +AIAssistancePanel.enableHintCheckBox.text=Enable Hints +AIAssistancePanel.aiAssistantActivationCheckBox.toolTipText=Turn the AI assistant on or off to enable smart code completion and suggestions for a better coding experience. +AIAssistancePanel.aiAssistantActivationCheckBox.text=Activate AI Assistant +AIAssistancePanel.modelsInfo.text= AIAssistancePanel.modelComboBox.toolTipText=Choose the specific Model for AI assistant. Models may vary in complexity, speed, and token limits, allowing you to select the best fit for your task. -AIAssistancePanel.providerLabel.text=Provider: -AIAssistancePanel.providerLocationField.text= +AIAssistancePanel.gptModelHelp.text=Choose a model based on your performance and accuracy requirements. +AIAssistancePanel.gptModelLabel.text=Model: AIAssistancePanel.apiKeyField.text= +AIAssistancePanel.apiKeyInfo.text= AIAssistancePanel.apiKeyLabel.text=API Key: +AIAssistancePanel.providerLocationField.text= AIAssistancePanel.providerLocationLabel.text=URL: -AIAssistancePanel.modelsInfo.text= -AIAssistancePanel.apiKeyInfo.text= +AIAssistancePanel.providerComboBox.toolTipText=Select the Provider for AI assistant. Providers can offer different types of models and capabilities, such as speed, context size, and accuracy. +AIAssistancePanel.providerLabel.text=Provider: AIAssistancePanel.providersPane.TabConstraints.tabTitle=Providers -AIAssistancePanel.inlineCompletionPane.TabConstraints.tabTitle=Inline Completion -AIAssistancePanel.excludeJavadocCommentsCheckBox.text=Exclude Javadoc Comments in Context -AIAssistancePanel.excludeJavadocCommentsCheckBox.toolTipText=Exclude Javadoc comments when generating context to focus solely on code and reduce token usage for better performance. Helps improve accuracy and avoid irrelevant suggestions. -AIAssistancePanel.hintPane.TabConstraints.tabTitle=Hints -AIAssistancePanel.testPromptLabel.text=Generate Test Case Prompt: -AIAssistancePanel.testPromptField.text=Type the prompt for generating test cases based on your class context or project. -AIAssistancePanel.varContextComboBox.toolTipText=Select the classes to share with the AI assistant for enhanced context and more accurate suggestions. -AIAssistancePanel.varContextLabel.text=Class Context Scope for Variable Name, Method Name, String Literals: -AIAssistancePanel.varContextHelp.text=Provide context for the assistant to suggest better variable names, method names, and strings based on your code structure. -AIAssistancePanel.fileExtLabel.text=File Extensions to Include in Context: -AIAssistancePanel.askAIPane.TabConstraints.tabTitle=AI Chat -AIAssistancePanel.fileExtField.text=e.g., java, xml, yaml -AIAssistancePanel.jLabel1.text=(This setting applies at both Project and Folder Levels) -AIAssistancePanel.excludeDir.text=Directories and Files to Exclude from Context (This setting is applicable at the Project Level) +AIAssistancePanel.enableStreamCheckbox.toolTipText=Activating streaming mode allows for continuous data processing, which enhances the responsiveness of the AI assistant, making the chat experience feel more dynamic and interactive. +AIAssistancePanel.enableStreamCheckbox.text=Enable Stream