feat(dynform): structural validation and generator enhancements

- Implemented DynFormAnnotator to validate field usage in <LAYOUT> and <FILTERS> against their respective <FIELDS> definitions.
- Refined ActionBean generator logic for NUMBER and DATE column width calculation.
- Improved window management and focus for newly generated files using FileEditorManagerEx.
- Bumped plugin version to 3.2.6.
This commit is contained in:
2026-04-18 12:57:07 +07:00
parent d2d2d4d642
commit ffe1712318
4 changed files with 103 additions and 13 deletions

View File

@@ -4,7 +4,7 @@ plugins {
id("org.jetbrains.intellij.platform") version "2.7.0" id("org.jetbrains.intellij.platform") version "2.7.0"
} }
group = "com.sdk.dynform.tools" group = "com.sdk.dynform.tools"
version = "3.2.5" version = "3.2.6"
repositories { repositories {
mavenCentral() mavenCentral()
@@ -38,19 +38,18 @@ intellijPlatform {
} }
changeNotes = """ changeNotes = """
<h2>[3.2.6]</h2>
<ul>
<li><strong>Structural Validation:</strong> Introduced a new Annotator to validate that all fields used in <code>&lt;LAYOUT&gt;</code> and <code>&lt;FILTERS&gt;</code> are correctly defined in their respective <code>&lt;FIELDS&gt;</code> sections.</li>
<li><strong>Generator Logic Update:</strong> Refined the column width calculation for NUMBER and DATE types in the ActionBean generator for better XML compatibility.</li>
<li><strong>Improved File Opening:</strong> Switched to <code>FileEditorManagerEx</code> for opening generated files, ensuring better window focus and management in recent IDE versions.</li>
</ul>
<h2>[3.2.5]</h2> <h2>[3.2.5]</h2>
<ul> <ul>
<li><strong>Dataset to Form Mapping:</strong> Implemented comprehensive reference and completion support for the <code>FORM-NAME</code> attribute in <code>DATASET > FIELDS > FIELD</code>.</li> <li><strong>Dataset to Form Mapping:</strong> Implemented comprehensive reference and completion support for the <code>FORM-NAME</code> attribute in <code>DATASET > FIELDS > FIELD</code>.</li>
<li><strong>Smart Field Resolution:</strong> <code>FORM-NAME</code> now correctly resolves to hidden fields in <code>FORM_ENTRY > FIELDS</code> and any named fields within <code>FORM_ENTRY > LAYOUT</code>.</li> <li><strong>Smart Field Resolution:</strong> <code>FORM-NAME</code> now correctly resolves to hidden fields in <code>FORM_ENTRY > FIELDS</code> and any named fields within <code>FORM_ENTRY > LAYOUT</code>.</li>
<li><strong>Recursive Form Scanning:</strong> Enhanced field discovery to scan across all form entries in the current and recursively included <code>.frml</code> files.</li> <li><strong>Recursive Form Scanning:</strong> Enhanced field discovery to scan across all form entries in the current and recursively included <code>.frml</code> files.</li>
</ul> </ul>
<h2>[3.2.4]</h2>
<ul>
<li><strong>Persistent Generation Directories:</strong> Generator now remembers the last used directory for Action Beans and Dataset XMLs independently per project.</li>
<li><strong>Auto-Open Generated Files:</strong> Added configuration to automatically open newly created files in the editor, with a customizable limit on the number of files.</li>
<li><strong>Strict Project-Relative Paths:</strong> I18n and XSD configuration now strictly enforces file selection within the project root and stores paths as relative for maximum portability.</li>
<li><strong>Smart File Browser:</strong> Improved file picker logic to automatically start at the current configured directory if it's within the project, falling back to the project root otherwise.</li>
</ul>
<h2>[3.2.3]</h2> <h2>[3.2.3]</h2>
<ul> <ul>
<li><strong>Advanced Data Referencing:</strong> Implemented comprehensive reference and completion support for <code>&lt;FOREIGN-DATASETS&gt;</code> and <code>&lt;MASTER-DATA&gt;</code> structures.</li> <li><strong>Advanced Data Referencing:</strong> Implemented comprehensive reference and completion support for <code>&lt;FOREIGN-DATASETS&gt;</code> and <code>&lt;MASTER-DATA&gt;</code> structures.</li>

View File

@@ -5,7 +5,7 @@ import com.intellij.database.model.DasTableKey;
import com.intellij.database.psi.DbTable; import com.intellij.database.psi.DbTable;
import com.intellij.database.util.DasUtil; import com.intellij.database.util.DasUtil;
import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFile;
@@ -62,7 +62,9 @@ public class GeneratorServices {
private void openInEditor(VirtualFile file) { private void openInEditor(VirtualFile file) {
if (file != null) { if (file != null) {
ApplicationManager.getApplication().invokeLater(() -> { ApplicationManager.getApplication().invokeLater(() -> {
FileEditorManager.getInstance(project).openFile(file, true); FileEditorManagerEx manager = FileEditorManagerEx.getInstanceEx(project);
// Using 3-parameter openFile from FileEditorManagerEx to ensure it's handled correctly
manager.openFile(file, true, true);
}); });
} }
} }
@@ -224,16 +226,19 @@ public class GeneratorServices {
colModel.put("isPk", primaryKeys.contains(colName)); colModel.put("isPk", primaryKeys.contains(colName));
colModel.put("customType", dataType); colModel.put("customType", dataType);
int width = column.getDasType().toDataType().getLength();
// XML Specific fields // XML Specific fields
String xmlType = "TEXT"; String xmlType = "TEXT";
if ("NUMBER".equals(dataType)) { if ("NUMBER".equals(dataType)) {
xmlType = "NUMBER"; xmlType = "NUMBER";
width = String.valueOf(width).length();
} else if ("DATE".equals(dataType)) { } else if ("DATE".equals(dataType)) {
xmlType = "DATE"; xmlType = "DATE";
width = "dd/mm/yyyy hh:mm:ss".length();
} }
colModel.put("xmlType", xmlType);
int width = column.getDasType().toDataType().getLength(); colModel.put("xmlType", xmlType);
colModel.put("width", Math.max(width, 0)); colModel.put("width", Math.max(width, 0));
// Label generation: Use field comment if available, otherwise fallback to generated key // Label generation: Use field comment if available, otherwise fallback to generated key
@@ -254,4 +259,4 @@ public class GeneratorServices {
return model; return model;
} }
} }

View File

@@ -0,0 +1,79 @@
package com.sdk.dynform.tools.dynform;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.Annotator;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.psi.PsiElement;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.psi.xml.XmlTag;
import org.jetbrains.annotations.NotNull;
public class DynFormAnnotator implements Annotator {
@Override
public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) {
if (!(element instanceof XmlAttributeValue attrValue)) return;
PsiElement parent = attrValue.getParent();
if (!(parent instanceof XmlAttribute attr)) return;
if (!"NAME".equals(attr.getName())) return;
PsiElement tagElem = attr.getParent();
if (!(tagElem instanceof XmlTag tag) || !"FIELD".equals(tag.getName())) return;
// Check if this FIELD is inside a LAYOUT tag
XmlTag layoutTag = tag.getParentTag();
while (layoutTag != null && !"LAYOUT".equals(layoutTag.getName())) {
layoutTag = layoutTag.getParentTag();
}
if (layoutTag == null) return;
// The "Owner" of the LAYOUT (e.g., FORM_ENTRY, FILTERS, GRID-EDITOR)
XmlTag ownerTag = layoutTag.getParentTag();
if (ownerTag == null) return;
String fieldName = attrValue.getValue();
if (fieldName == null || fieldName.isEmpty()) return;
// Look for the FIELDS tag that is a sibling of the current LAYOUT
XmlTag fieldsTag = ownerTag.findFirstSubTag("FIELDS");
if (fieldsTag == null) {
holder.newAnnotation(HighlightSeverity.ERROR, "No <FIELDS> definition found in <" + ownerTag.getName() + ">")
.range(attrValue.getTextRange())
.create();
return;
}
if (!isFieldDefined(fieldsTag, fieldName)) {
holder.newAnnotation(HighlightSeverity.ERROR, "Field '" + fieldName + "' is not defined in <FIELDS> of <" + ownerTag.getName() + ">")
.range(attrValue.getTextRange())
.create();
}
}
private boolean isFieldDefined(XmlTag fieldsTag, String name) {
for (XmlTag field : fieldsTag.findSubTags("FIELD")) {
if (name.equals(field.getAttributeValue("NAME"))) {
return true;
}
}
// Also check inside SECTIONS or ROWS if any
for (XmlTag sub : fieldsTag.getSubTags()) {
if ("SECTION".equals(sub.getName()) || "ROW".equals(sub.getName())) {
if (isFieldDefined(sub, name)) return true;
}
}
return false;
}
private static boolean hasAncestorWithName(XmlTag tag, String name) {
XmlTag current = tag.getParentTag();
while (current != null) {
if (name.equals(current.getName())) return true;
current = current.getParentTag();
}
return false;
}
}

View File

@@ -34,6 +34,12 @@
]]></description> ]]></description>
<change-notes><![CDATA[ <change-notes><![CDATA[
<h2>[3.2.6]</h2>
<ul>
<li><strong>Structural Validation:</strong> Introduced a new Annotator to validate that all fields used in <code>&lt;LAYOUT&gt;</code> and <code>&lt;FILTERS&gt;</code> are correctly defined in their respective <code>&lt;FIELDS&gt;</code> sections.</li>
<li><strong>Generator Logic Update:</strong> Refined the column width calculation for NUMBER and DATE types in the ActionBean generator for better XML compatibility.</li>
<li><strong>Improved File Opening:</strong> Switched to <code>FileEditorManagerEx</code> for opening generated files, ensuring better window focus and management in recent IDE versions.</li>
</ul>
<h2>[3.2.5]</h2> <h2>[3.2.5]</h2>
<ul> <ul>
<li><strong>Dataset to Form Mapping:</strong> Implemented comprehensive reference and completion support for the <code>FORM-NAME</code> attribute in <code>DATASET > FIELDS > FIELD</code>.</li> <li><strong>Dataset to Form Mapping:</strong> Implemented comprehensive reference and completion support for the <code>FORM-NAME</code> attribute in <code>DATASET > FIELDS > FIELD</code>.</li>
@@ -188,5 +194,6 @@
<completion.contributor language="XML" implementationClass="com.sdk.dynform.tools.dynform.DynFormCompletionContributor"/> <completion.contributor language="XML" implementationClass="com.sdk.dynform.tools.dynform.DynFormCompletionContributor"/>
<completion.contributor language="JavaScript" implementationClass="com.sdk.dynform.tools.dynform.DynFormCompletionContributor"/> <completion.contributor language="JavaScript" implementationClass="com.sdk.dynform.tools.dynform.DynFormCompletionContributor"/>
<annotator language="XML" implementationClass="com.sdk.dynform.tools.dynform.DynFormAnnotator"/>
</extensions> </extensions>
</idea-plugin> </idea-plugin>