Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
6ac2a72
feat: Implement #9: Show Jeddict icon in NetBeans menus
jGauravGupta Aug 24, 2025
1444d29
added headers and unified formatting
stefanofornari Aug 29, 2025
9fd8a42
- removed the use of an internal class with our dedicated DiffView
stefanofornari Sep 1, 2025
1ef65ff
include test directory
stefanofornari Sep 1, 2025
b780136
A StreamSource on top of a FileObject. This class provides basic impl…
stefanofornari Sep 1, 2025
d162516
A DiffView nspired by org.netbeans.modules.diff.builtin.SingleDiffPanel
stefanofornari Sep 1, 2025
128e680
A base class for context-aware actions that are presented as popup me…
stefanofornari Sep 1, 2025
77f8b00
Added close button
stefanofornari Sep 1, 2025
01fd570
Added close button
stefanofornari Sep 1, 2025
59a0d56
moved into local resources
stefanofornari Sep 1, 2025
5a01652
added unit testing framework
stefanofornari Sep 1, 2025
1717103
Added close button
stefanofornari Sep 1, 2025
b38b00b
Merge pull request #14 from stefanofornari/ste.#9.action_icons
stefanofornari Sep 1, 2025
a1023c8
Using DiffUtil.diffWithOriginal in the case of diffing an entire class
stefanofornari Sep 1, 2025
a8093c6
Merge origin/main into ste.#11.review-diff
stefanofornari Sep 2, 2025
eae64dd
cleanup to prepare the PR
stefanofornari Sep 2, 2025
42d605b
Merge pull request #16 from stefanofornari/ste.diffview-cleanup
stefanofornari Sep 2, 2025
e694ac3
Added a separate action for the toolbar
stefanofornari Sep 3, 2025
f0788b8
renamed BaseContextAction into BaseMenuAction
stefanofornari Sep 3, 2025
3457c03
Merge branch 'ste.#9.action_icons' to fix action's icon in toolbar.
stefanofornari Sep 3, 2025
d81d58f
fixed typo
stefanofornari Sep 3, 2025
5270cd6
cleanup
stefanofornari Sep 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,3 @@
hs_err_pid*
replay_pid*
/target/
test
60 changes: 53 additions & 7 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,18 @@
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
<configuration>
<includes>
<include>**/*Test.java</include>
</includes>
<forkCount>1</forkCount>
<reuseForks>false</reuseForks>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
Expand Down Expand Up @@ -516,12 +528,12 @@
<groupId>org.netbeans.modules</groupId>
<artifactId>org-netbeans-modules-maven-embedder</artifactId>
<version>RELEASE270</version>
<exclusions>
<exclusion>
<groupId>org.jspecify.jspecify</groupId>
<artifactId>jspecify</artifactId>
</exclusion>
</exclusions>
<exclusions>
<exclusion>
<groupId>org.jspecify.jspecify</groupId>
<artifactId>jspecify</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.netbeans.api</groupId>
Expand Down Expand Up @@ -558,6 +570,40 @@
<artifactId>org-openide-io</artifactId>
<version>RELEASE270</version>
</dependency>
<dependency>
<groupId>org.netbeans.api</groupId>
<artifactId>org-netbeans-modules-nbjunit</artifactId>
<version>RELEASE270</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.netbeans.api</groupId>
<artifactId>org-netbeans-modules-queries</artifactId>
<version>RELEASE270</version>
</dependency>
<dependency>
<groupId>org.netbeans.api</groupId>
<artifactId>org-openide-actions</artifactId>
<version>RELEASE270</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.27.4</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
Expand Down Expand Up @@ -662,7 +708,7 @@
</distributionManagement>
</profile>
</profiles>

<dependencyManagement>
<dependencies>
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
import io.github.jeddict.ai.hints.AssistantChatManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.AbstractAction;
import static javax.swing.Action.NAME;
import javax.swing.JOptionPane;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
Expand All @@ -37,26 +35,42 @@
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;

/**
* A popup action that provides AI assistance in the editor. This action is
* available in the editor's context menu and can be invoked with a shortcut.
* It opens a chat window with the selected text and allows the user to interact
* with the AI assistant to modify the code.
*/
@ActionID(
category = "Edit/Chat",
id = "io.github.jeddict.ai.actions.AIAssistantPopupAction"
category = "Edit/Chat",
id = "io.github.jeddict.ai.actions.AIAssistantPopupAction"
)
@ActionRegistration(
displayName = "#CTL_AIAssistantPopupAction", lazy = false
displayName = "#CTL_AIAssistantPopupAction",
lazy = false,
iconInMenu = true,
iconBase = "icons/logo24.png"
)
@ActionReferences({
@ActionReference(path = "Editors/Popup", position = 101),
@ActionReference(path = "Shortcuts", name = "C-QUOTE")
})
@NbBundle.Messages("CTL_AIAssistantPopupAction=AI Assistant")
public final class AIAssistantPopupAction extends AbstractAction implements ActionListener {
public final class AIAssistantPopupAction extends BaseMenuAction implements ActionListener {

/**
* Constructs a new AIAssistantPopupAction.
*/
public AIAssistantPopupAction() {
putValue(NAME, Bundle.CTL_AIAssistantPopupAction());
setEnabled(true);
super(Bundle.CTL_AIAssistantPopupAction(), true);
}


/**
* Opens the AI assistant chat window with the selected text from the
* editor.
*
* @param e the action event.
*/
@Override
public void actionPerformed(ActionEvent e) {

Expand Down Expand Up @@ -101,6 +115,15 @@ public void actionPerformed(ActionEvent e) {
});
}

/**
* Inserts the given content into the document and reformats the inserted
* text.
*
* @param document the document to modify.
* @param content the content to insert.
* @param startPosition the position at which to insert the content.
* @param lengthToRemove the length of the text to remove.
*/
private void insertAndReformat(StyledDocument document, String content, int startPosition, int lengthToRemove) {
try {
if (lengthToRemove > 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,21 @@
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;

/**
* An action that adds the selected file(s) or folder(s) to the current AI
* chat session context. This action is available in the context menu of files
* and folders in the Projects view.
*/
@ActionID(
category = "Project",
id = "io.github.jeddict.ai.actions.AddToChatContextAction"
category = "Project",
id = "io.github.jeddict.ai.actions.AddToChatContextAction"
)
@ActionRegistration(
displayName = "#CTL_AddToChatContextAction",
lazy = false,
asynchronous = false
displayName = "#CTL_AddToChatContextAction",
lazy = false,
iconInMenu = true,
iconBase = "icons/logo16.png",
asynchronous = false
)
@ActionReferences({
@ActionReference(path = "Projects/package/Actions", position = 101),
Expand All @@ -49,23 +56,45 @@
@Messages({"CTL_AddToChatContextAction=Add to Chat Session Context"})
public final class AddToChatContextAction extends AbstractAction implements ContextAwareAction {

/**
* This method is never called directly. The action is handled by the
* context-aware instance.
*
* @param e the action event.
*/
@Override
public void actionPerformed(ActionEvent e) {
// nikdy nebude volaná priamo - len cez ContextAction
// It will never be called directly—only through ContextAction
}

/**
* Creates a context-aware instance of this action.
*
* @param context the lookup context.
* @return a new instance of the context-aware action.
*/
@Override
public Action createContextAwareInstance(Lookup context) {
List<FileObject> files = new ArrayList<>(context.lookupAll(FileObject.class));
boolean en = !files.isEmpty() && isAssistantChatOpened();
return new ContextAction(en, files);
}

/**
* Returns true if the AI assistant chat is opened, false otherwise.
*
* @return true if the AI assistant chat is opened, false otherwise.
*/
@Override
public boolean isEnabled() {
return isAssistantChatOpened();
}

/**
* Checks if the AI assistant chat window is opened and showing.
*
* @return true if the chat window is opened and showing, false otherwise.
*/
private static boolean isAssistantChatOpened() {
Set<? extends Mode> modes = WindowManager.getDefault().getModes();
for (Mode mode : modes) {
Expand All @@ -81,6 +110,11 @@ private static boolean isAssistantChatOpened() {
return false;
}

/**
* Returns the opened AI assistant chat window.
*
* @return the opened chat window, or null if it is not opened.
*/
private static TopComponent getOpenedAssistantChat() {
Set<? extends Mode> modes = WindowManager.getDefault().getModes();
for (Mode mode : modes) {
Expand All @@ -96,17 +130,30 @@ private static TopComponent getOpenedAssistantChat() {
return null;
}

private static final class ContextAction extends AbstractAction {
/**
* The context-aware action that adds the selected files to the chat
* session.
*/
private static final class ContextAction extends BaseMenuAction {

private final List<FileObject> files;

/**
* Constructs a new ContextAction.
*
* @param enabled true to enable the action, false to disable it.
* @param files the list of files to add to the chat session.
*/
private ContextAction(boolean enabled, List<FileObject> files) {
super(Bundle.CTL_AddToChatContextAction());
super(Bundle.CTL_AddToChatContextAction(), enabled);
this.files = files;
this.putValue(DynamicMenuContent.HIDE_WHEN_DISABLED, true);
setEnabled(enabled);
}

/**
* Adds the selected files to the chat session context.
*
* @param e the action event.
*/
@Override
public void actionPerformed(ActionEvent e) {
TopComponent openedAssistantChat = getOpenedAssistantChat();
Expand All @@ -121,6 +168,11 @@ public void actionPerformed(ActionEvent e) {
}
}

/**
* Returns true if the AI assistant chat is opened, false otherwise.
*
* @return true if the AI assistant chat is opened, false otherwise.
*/
@Override
public boolean isEnabled() {
return isAssistantChatOpened();
Expand Down
51 changes: 43 additions & 8 deletions src/main/java/io/github/jeddict/ai/actions/AskAIDBAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,26 @@
import org.openide.awt.ActionReference;
import org.openide.awt.ActionReferences;
import org.openide.awt.ActionRegistration;
import org.openide.awt.DynamicMenuContent;
import org.openide.util.ContextAwareAction;
import org.openide.util.Lookup;
import org.openide.util.NbBundle.Messages;

/**
* An action that allows the user to ask AI questions about a database
* connection. This action is available in the context menu of a database
* connection in the Services window.
*/
@ActionID(
category = "File",
id = "io.github.jeddict.ai.actions.AskAIDBAction")
category = "File",
id = "io.github.jeddict.ai.actions.AskAIDBAction"
)
@ActionRegistration(
displayName = "#CTL_AskAIDBAction", lazy = false, asynchronous = true)
displayName = "#CTL_AskAIDBAction",
lazy = false,
asynchronous = true,
iconInMenu = true,
iconBase = "icons/logo16.png"
)
@ActionReferences({
@ActionReference(path = "Databases/Explorer/Connection/Actions", position = 350),
// @ActionReference(path = "Databases/Explorer/Catalog/Actions", position = 350),
Expand All @@ -50,10 +60,22 @@
@Messages({"CTL_AskAIDBAction=AI Assistant"})
public final class AskAIDBAction extends AbstractAction implements ContextAwareAction {

/**
* This method is never called directly. The action is handled by the
* context-aware instance.
*
* @param ev the action event.
*/
@Override
public void actionPerformed(ActionEvent ev) {
}

/**
* Creates a context-aware instance of this action.
*
* @param actionContext the lookup context.
* @return a new instance of the context-aware action.
*/
@Override
public Action createContextAwareInstance(Lookup actionContext) {
if (actionContext != null) {
Expand All @@ -65,17 +87,30 @@ public Action createContextAwareInstance(Lookup actionContext) {
return new AskAIDBAction.ContextAction(false, null);
}

private static final class ContextAction extends AbstractAction {
/**
* The context-aware action that opens the AI chat window for the selected
* database connection.
*/
private static final class ContextAction extends BaseMenuAction {

private final DatabaseConnection connection;

/**
* Constructs a new ContextAction.
*
* @param enable true to enable the action, false to disable it.
* @param connection the database connection.
*/
private ContextAction(boolean enable, DatabaseConnection connection) {
super(Bundle.CTL_AskAIDBAction());
this.putValue(DynamicMenuContent.HIDE_WHEN_DISABLED, true);
this.setEnabled(enable);
super(Bundle.CTL_AskAIDBAction(), enable);
this.connection = connection;
}

/**
* Opens the AI chat window for the selected database connection.
*
* @param evt the action event.
*/
@Override
public void actionPerformed(ActionEvent evt) {
if (connection == null) {
Expand Down
Loading