Skip to content

Add MCP regenerate_form_code(path) to regenerate modified swing-based forms in Netbeans - #192

Draft
Rizky92 wants to merge 15 commits into
nbplugins:mainfrom
Rizky92:feature/regenerate-form-code
Draft

Rizky92 wants to merge 15 commits into
nbplugins:mainfrom
Rizky92:feature/regenerate-form-code

Conversation

@Rizky92

@Rizky92 Rizky92 commented Oct 13, 2026

Copy link
Copy Markdown
Contributor

This PR added MCP regenerate_form_code(path). The purpose is to allow Claude Code reload the swing-based module after modifying the design form. Support for adding generated event block is not available, as it's separate concerns and maybe for the future.

Tested on Netbeans-31, windows 10 22H2. Some local unit tests was skipped because my environment is not supported.

… forms in netbeans.

Signed-off-by: Muhammad Rizky Rizaldi <myquinn489@gmail.com>

@oleg68 oleg68 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! The feature fits the plugin well (an IDE-only capability exposed over MCP) and the tests are thorough. Main concerns: this is the first tool that drives a non-API NetBeans module through reflection, and the tool is auto-allowed although it overwrites two project files. Items 1, 3 and 8 are required for merge; the rest are suggestions.

1. ClaudeProcess.java — OUR_ALLOWED_TOOLS (required)

The tool overwrites .java and re-serializes .form, bypassing the plugin's diff/permission UI. Unlike show_markdown* it is not read-only. Please don't auto-allow it; let Claude Code's normal permission prompt apply:

            "mcp__netbeans__show_markdown",
            "mcp__netbeans__show_markdown_file"

If auto-allow is wanted, make it an opt-in setting (default off) and document it.

2. RegenerateFormCode — verify the reflective API up front

The sequence can fail midway (form loaded, fireFormChanged sent, then a method is missing). Resolve all required methods (isOpened, loadForm, reloadFormEditor, getFormEditor, getDocument, saveDocument, ...) before touching the form, so an unsupported NetBeans version fails with "Unsupported NetBeans form editor version" before any state changes. Please also document the NetBeans versions this was verified on (31 per the description).

3. regenerateInFormEditor(...) — always close the form (required)

When the designer was closed, closeForm is skipped if fireFormChanged, regenerateCodeIfNeeded or saveDocument throws, leaking the loaded form. Suggestion: wrap the tail in try/finally and drop the early closeForm branches:

try {
    call(formModel, "fireFormChanged", boolean.class, true);
    call(formEditor, "regenerateCodeIfNeeded");
    String after = documentText(document);
    call(editorSupport, "saveDocument");
    return new Outcome(open, !before.equals(after), after);
} finally {
    if (!open) {
        call(formEditor, "closeForm");
    }
}

4. call(...) — non-public runtime class

getClass().getMethod(...) + invoke throws IllegalAccessException if the runtime class is not public. Map it to the same "Unsupported NetBeans form editor version" error:

} catch (IllegalAccessException e) {
    throw new IllegalStateException("Unsupported NetBeans form editor version: "
            + target.getClass().getName() + "." + methodName + "() is not accessible", e);
}

5. EDT work

saveDocument (disk I/O + formatting) runs inside invokeAndWait. If unavoidable per the form API's threading rules, please say so in the Javadoc.

6. waitForDocumentInSync / documentMatchesDisk

The whitespace-insensitive comparison treats whitespace-only external edits as "in sync". Fine, but please note it in the Javadoc, and that the method must never be called on the EDT (Thread.sleep).

7. checkAccess

The path comes straight from the model. Please confirm NbUtils.isPathWithinOpenProjects normalizes .. and symlinks; if not, use FileUtil.normalizeFile / toRealPath() first.

8. Manual test plan (required)

The fake-object tests prove the call sequence, not that real NetBeans behaves that way. Please add a manual test plan to the PR: closed designer, open designer, malformed .form, .java with unsaved changes, NetBeans without the form module. Also mention which tests were skipped and why.

9. pom.xml

1.3.53 -> 1.3.55; the project rule is +1 patch:

<version>1.3.54-SNAPSHOT</version>

10. CHANGELOG.md wording

Entries should describe the change from the user's perspective, without implementation details:

- Added the `regenerate_form_code` tool: after Claude edits a Swing form (`.form`), it can regenerate the form's code in the IDE instead of editing the generated code by hand

11. Commit message / PR title

Per CLAUDE.md a past-tense verb is expected, e.g. "Added MCP regenerate_form_code tool to regenerate Swing form code". Also, the parameter is filePath, not path.

12. docs/user-manual.md

Please add a short limitation note: the tool relies on NetBeans form-module internals and may stop working after an IDE update; it requires the Swing GUI Builder to be enabled.

@oleg68 oleg68 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! The feature fits the plugin well (an IDE-only capability exposed over MCP) and the tests are thorough. Main concerns: this is the first tool that drives a non-API NetBeans module through reflection, and it is auto-allowed although it overwrites two project files. Inline comments marked (required) should be addressed before merge; the rest are suggestions.

Also required:

  • Manual test plan. The fake-object tests prove the call sequence, not that real NetBeans behaves that way. Please add a manual test plan to the PR description: closed designer, open designer, malformed .form, .java with unsaved changes, NetBeans without the form module. Mention which tests were skipped and why.

Minor:

  • Commit message / PR title: CLAUDE.md requires a past-tense verb, e.g. "Added MCP regenerate_form_code tool to regenerate Swing form code". The parameter is filePath, not path.

Comment on lines +67 to +68
"mcp__netbeans__show_markdown_file",
"mcp__netbeans__regenerate_form_code"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(required) This tool overwrites .java and re-serializes .form, bypassing the plugin's diff/permission UI. Unlike show_markdown* it is not read-only. Please don't auto-allow it; let Claude Code's normal permission prompt apply. If auto-allow is wanted, make it an opt-in setting (default off) and document it.

Suggested change
"mcp__netbeans__show_markdown_file",
"mcp__netbeans__regenerate_form_code"
"mcp__netbeans__show_markdown_file"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed from allowlist 3ff268e.

}

/** Returns the form module's {@code EditorSupport}, or {@code null} for a non-form data object. */
private static Object formEditorSupport(DataObject dataObject) throws Exception {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolve all reflectively used methods (isOpened, loadForm, reloadFormEditor, getFormEditor, getDocument, saveDocument, ...) up front, here, before touching the form. Now the sequence can fail midway (form loaded, fireFormChanged sent, then a method is missing). An unsupported NetBeans version should fail with "Unsupported NetBeans form editor version" before any state changes. Please also document the NetBeans versions this was verified on (31 per the description).

*
* @param file file to check
*/
protected void checkAccess(File file) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The path comes straight from the model. Please confirm NbUtils.isPathWithinOpenProjects normalizes .. and symlinks; if not, normalize (FileUtil.normalizeFile / toRealPath()) before the check.

}
Document document = (Document) call(editorSupport, "getDocument");
String before = documentText(document);
call(formModel, "fireFormChanged", boolean.class, true);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(required) When the designer was closed, closeForm is skipped if fireFormChanged, regenerateCodeIfNeeded or saveDocument throws, leaking the loaded form. Wrap this tail in try/finally:

Suggested change
call(formModel, "fireFormChanged", boolean.class, true);
try {
call(formModel, "fireFormChanged", boolean.class, true);
call(formEditor, "regenerateCodeIfNeeded");
String after = documentText(document);
call(editorSupport, "saveDocument");
return new Outcome(open, !before.equals(after), after);
} finally {
if (!open) {
call(formEditor, "closeForm");
}
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved here dc77aa3.

* asynchronously. Waits until it shows the content on disk so the regenerated
* code is not merged into, and saved over, a stale document.
*/
private static void waitForDocumentInSync(EditorCookie editorCookie, FileObject javaFo, String javaName)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two notes for the Javadoc: the whitespace-insensitive comparison treats whitespace-only external edits as "in sync" (acceptable, but worth stating), and this method uses Thread.sleep so it must never be called on the EDT.

}
try {
return paramType == null ? method.invoke(target) : method.invoke(target, arg);
} catch (InvocationTargetException e) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getMethod + invoke throws IllegalAccessException if the runtime class is not public. Map it to the same "Unsupported NetBeans form editor version" error:

Suggested change
} catch (InvocationTargetException e) {
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unsupported NetBeans form editor version: "
+ target.getClass().getName() + "." + methodName + "() is not accessible", e);
} catch (InvocationTargetException e) {

}
AtomicReference<T> result = new AtomicReference<>();
AtomicReference<Exception> error = new AtomicReference<>();
EventQueue.invokeAndWait(() -> {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

saveDocument (disk I/O + formatting) runs inside this invokeAndWait, so a slow disk blocks the IDE. If unavoidable per the form API's threading rules, please say so in the Javadoc and keep validation/waiting outside the EDT (as is already done).

Comment thread pom.xml Outdated
<groupId>io.github.nbplugins</groupId>
<artifactId>netbeans-plugin-claude-code-gui</artifactId>
<version>1.3.53-SNAPSHOT</version>
<version>1.3.55-SNAPSHOT</version>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The project rule is a +1 patch bump (main is at 1.3.53).

Suggested change
<version>1.3.55-SNAPSHOT</version>
<version>1.3.54-SNAPSHOT</version>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll keep this +1 of target branch instead.

Comment thread CHANGELOG.md Outdated
@@ -1,3 +1,4 @@
- Added the `regenerate_form_code` MCP tool: after Claude edits a Swing GUI Builder `.form` file, it regenerates the form's `initComponents()` and variable declarations in the `.java` file exactly as the NetBeans GUI Builder does and saves the form, instead of Claude hand-editing the generated code; an open GUI Builder designer is reloaded from the edited `.form`, and Claude is told when the generated code was already up to date

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per CLAUDE.md, entries describe the change from the user's perspective, without implementation details:

Suggested change
- Added the `regenerate_form_code` MCP tool: after Claude edits a Swing GUI Builder `.form` file, it regenerates the form's `initComponents()` and variable declarations in the `.java` file exactly as the NetBeans GUI Builder does and saves the form, instead of Claude hand-editing the generated code; an open GUI Builder designer is reloaded from the edited `.form`, and Claude is told when the generated code was already up to date
- Added the `regenerate_form_code` MCP tool: after Claude edits a Swing form (`.form`), it can regenerate the form's code in the IDE instead of editing the generated code by hand

Comment thread docs/user-manual.md
| `permission_prompt` | Shows proposed file changes in the diff panel and waits for your approval |
| `show_markdown` | Renders markdown text directly in a Markdown Preview tab; accepts the markdown content plus optional title and file path |
| `show_markdown_file` | Opens a live-updating Markdown Preview tab for an existing `.md` file on disk; the tab automatically refreshes when the file changes |
| `regenerate_form_code` | Regenerates the GUI Builder code (`initComponents()` and the variables declaration) of a Swing form's `.java` file from its edited `.form` file, exactly as the NetBeans GUI Builder does, and saves the form (NetBeans re-saves the `.form` in its own formatting). Claude calls it after changing a `.form` file. An open designer is reloaded from it. Refused if the form has unsaved changes in the IDE |

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a short limitation note: the tool relies on NetBeans form-module internals and may stop working after an IDE update; it requires the Swing GUI Builder to be enabled.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be applicable to introduce integration test towards generated code?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it would be nice.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Integration setup here 414faf5 and implementation details here 3c7c260.

@Rizky92
Rizky92 marked this pull request as draft October 20, 2026 00:22
@Rizky92

Rizky92 commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

Drafting while I resolve.

Signed-off-by: Muhammad Rizky Rizaldi <myquinn489@gmail.com>
Signed-off-by: Muhammad Rizky Rizaldi <myquinn489@gmail.com>
Signed-off-by: Muhammad Rizky Rizaldi <myquinn489@gmail.com>
Signed-off-by: Muhammad Rizky Rizaldi <myquinn489@gmail.com>
Signed-off-by: Muhammad Rizky Rizaldi <myquinn489@gmail.com>
Signed-off-by: Muhammad Rizky Rizaldi <myquinn489@gmail.com>
Signed-off-by: Muhammad Rizky Rizaldi <myquinn489@gmail.com>
Signed-off-by: Muhammad Rizky Rizaldi <myquinn489@gmail.com>
Signed-off-by: Muhammad Rizky Rizaldi <myquinn489@gmail.com>
Signed-off-by: Muhammad Rizky Rizaldi <myquinn489@gmail.com>
Signed-off-by: Muhammad Rizky Rizaldi <myquinn489@gmail.com>
Signed-off-by: Muhammad Rizky Rizaldi <myquinn489@gmail.com>
@Rizky92
Rizky92 force-pushed the feature/regenerate-form-code branch from d6679c4 to 7655353 Compare October 28, 2026 14:12
Signed-off-by: Muhammad Rizky Rizaldi <myquinn489@gmail.com>
Signed-off-by: Muhammad Rizky Rizaldi <myquinn489@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants