Repository navigation
Conversation
… forms in netbeans. Signed-off-by: Muhammad Rizky Rizaldi <myquinn489@gmail.com>
oleg68
left a comment
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
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,.javawith 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_codetool to regenerate Swing form code". The parameter isfilePath, notpath.
| "mcp__netbeans__show_markdown_file", | ||
| "mcp__netbeans__regenerate_form_code" |
There was a problem hiding this comment.
(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.
| "mcp__netbeans__show_markdown_file", | |
| "mcp__netbeans__regenerate_form_code" | |
| "mcp__netbeans__show_markdown_file" |
| } | ||
|
|
||
| /** Returns the form module's {@code EditorSupport}, or {@code null} for a non-form data object. */ | ||
| private static Object formEditorSupport(DataObject dataObject) throws Exception { |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
(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:
| 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"); | |
| } | |
| } |
| * 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) |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
getMethod + invoke throws IllegalAccessException if the runtime class is not public. Map it to the same "Unsupported NetBeans form editor version" error:
| } 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(() -> { |
There was a problem hiding this comment.
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).
| <groupId>io.github.nbplugins</groupId> | ||
| <artifactId>netbeans-plugin-claude-code-gui</artifactId> | ||
| <version>1.3.53-SNAPSHOT</version> | ||
| <version>1.3.55-SNAPSHOT</version> |
There was a problem hiding this comment.
The project rule is a +1 patch bump (main is at 1.3.53).
| <version>1.3.55-SNAPSHOT</version> | |
| <version>1.3.54-SNAPSHOT</version> |
There was a problem hiding this comment.
I'll keep this +1 of target branch instead.
| @@ -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 | |||
There was a problem hiding this comment.
Per CLAUDE.md, entries describe the change from the user's perspective, without implementation details:
| - 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 |
| | `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 | |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Would it be applicable to introduce integration test towards generated code?
|
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>
d6679c4 to
7655353
Compare
Signed-off-by: Muhammad Rizky Rizaldi <myquinn489@gmail.com>
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.