feat(dynform): reference and completion for FORM-NAME attribute
- Added comprehensive reference and completion support for FORM-NAME in DATASET > FIELDS > FIELD. - Implemented smart field resolution to target hidden fields in FORM_ENTRY > FIELDS and named fields in FORM_ENTRY > LAYOUT. - Enhanced form scanning to recursively discover fields across all included .frml files. - Bumped plugin version to 3.2.5.
This commit is contained in:
@@ -4,7 +4,7 @@ plugins {
|
||||
id("org.jetbrains.intellij.platform") version "2.7.0"
|
||||
}
|
||||
group = "com.sdk.dynform.tools"
|
||||
version = "3.2.4"
|
||||
version = "3.2.5"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
@@ -38,6 +38,12 @@ intellijPlatform {
|
||||
}
|
||||
|
||||
changeNotes = """
|
||||
<h2>[3.2.5]</h2>
|
||||
<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>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>
|
||||
</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>
|
||||
|
||||
@@ -213,6 +213,22 @@ public class DynFormCompletionContributor extends CompletionContributor {
|
||||
addGridsInFileRecursive(parameters.getOriginalFile(), resultSet, new HashSet<>());
|
||||
}
|
||||
});
|
||||
|
||||
// XML completion for FORM-NAME in DATASET > FIELDS > FIELD
|
||||
extend(CompletionType.BASIC, XmlPatterns.psiElement()
|
||||
.inside(XmlPatterns.xmlAttributeValue()
|
||||
.withParent(XmlPatterns.xmlAttribute().withName("FORM-NAME")
|
||||
.withParent(XmlPatterns.xmlTag().withName("FIELD")
|
||||
.withParent(XmlPatterns.xmlTag().withName("FIELDS")
|
||||
.withParent(XmlPatterns.xmlTag().withName("DATASET")))))),
|
||||
new CompletionProvider<CompletionParameters>() {
|
||||
@Override
|
||||
protected void addCompletions(@NotNull CompletionParameters parameters,
|
||||
@NotNull ProcessingContext context,
|
||||
@NotNull CompletionResultSet resultSet) {
|
||||
addFormFieldsForNameRecursive(parameters.getOriginalFile(), resultSet, new HashSet<>());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void addFieldsInTagRecursive(XmlTag container, @NotNull CompletionResultSet resultSet) {
|
||||
@@ -438,4 +454,60 @@ public class DynFormCompletionContributor extends CompletionContributor {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void addFormFieldsForNameRecursive(PsiFile file, @NotNull CompletionResultSet resultSet, Set<PsiFile> visited) {
|
||||
if (file == null || !visited.add(file)) return;
|
||||
if (!(file instanceof XmlFile xmlFile)) return;
|
||||
XmlTag rootTag = xmlFile.getRootTag();
|
||||
if (rootTag == null) return;
|
||||
|
||||
// 1. Search in current file FORM_ENTRY tags
|
||||
for (XmlTag formTag : rootTag.findSubTags("FORM")) {
|
||||
for (XmlTag entryTag : formTag.findSubTags("FORM_ENTRY")) {
|
||||
// Collect hidden fields from FIELDS
|
||||
XmlTag fieldsTag = entryTag.findFirstSubTag("FIELDS");
|
||||
if (fieldsTag != null) {
|
||||
for (XmlTag fieldTag : fieldsTag.findSubTags("FIELD")) {
|
||||
if ("HIDDEN".equals(fieldTag.getAttributeValue("INPUTTYPE"))) {
|
||||
String name = fieldTag.getAttributeValue("NAME");
|
||||
if (name != null && !name.isEmpty()) {
|
||||
resultSet.addElement(LookupElementBuilder.create(name)
|
||||
.withIcon(com.intellij.icons.AllIcons.Nodes.Field)
|
||||
.withTypeText("Hidden Field"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Collect any field/tag with NAME from LAYOUT
|
||||
XmlTag layoutTag = entryTag.findFirstSubTag("LAYOUT");
|
||||
if (layoutTag != null) {
|
||||
addFieldsInLayoutRecursive(layoutTag, resultSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Search in included files
|
||||
XmlTag includesTag = rootTag.findFirstSubTag("INCLUDES");
|
||||
if (includesTag != null) {
|
||||
for (XmlTag includeTag : includesTag.findSubTags("INCLUDE")) {
|
||||
String path = includeTag.getAttributeValue("FILE");
|
||||
if (path != null) {
|
||||
PsiFile includedFile = DynFormPathUtils.findIncludedFile(file, path);
|
||||
addFormFieldsForNameRecursive(includedFile, resultSet, visited);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addFieldsInLayoutRecursive(XmlTag container, @NotNull CompletionResultSet resultSet) {
|
||||
for (XmlTag subTag : container.getSubTags()) {
|
||||
String name = subTag.getAttributeValue("NAME");
|
||||
if (name != null && !name.isEmpty()) {
|
||||
resultSet.addElement(LookupElementBuilder.create(name)
|
||||
.withIcon(com.intellij.icons.AllIcons.Nodes.Field)
|
||||
.withTypeText("Layout: " + subTag.getName()));
|
||||
}
|
||||
addFieldsInLayoutRecursive(subTag, resultSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +146,23 @@ public class DynFormReferenceContributor extends PsiReferenceContributor {
|
||||
}
|
||||
});
|
||||
|
||||
// XML Reference for FORM-NAME in DATASET > FIELDS > FIELD
|
||||
registrar.registerReferenceProvider(XmlPatterns.xmlAttributeValue()
|
||||
.withParent(XmlPatterns.xmlAttribute().withName("FORM-NAME")
|
||||
.withParent(XmlPatterns.xmlTag().withName("FIELD")
|
||||
.withParent(XmlPatterns.xmlTag().withName("FIELDS")
|
||||
.withParent(XmlPatterns.xmlTag().withName("DATASET"))))),
|
||||
new PsiReferenceProvider() {
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiReference @NotNull [] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) {
|
||||
XmlAttributeValue attrValue = (XmlAttributeValue) element;
|
||||
String value = attrValue.getValue();
|
||||
if (value == null || value.isEmpty()) return PsiReference.EMPTY_ARRAY;
|
||||
return new PsiReference[]{new DynFormFormNameReference(attrValue, new TextRange(1, value.length() + 1), value)};
|
||||
}
|
||||
});
|
||||
|
||||
// XML Reference for SRC in UPDATE-FIELDS
|
||||
registrar.registerReferenceProvider(XmlPatterns.xmlAttributeValue()
|
||||
.withParent(XmlPatterns.xmlAttribute().withName("SRC")
|
||||
@@ -662,4 +679,63 @@ public class DynFormReferenceContributor extends PsiReferenceContributor {
|
||||
return DynFormPathUtils.findIncludedFile(myElement.getContainingFile(), path);
|
||||
}
|
||||
}
|
||||
|
||||
private static class DynFormFormNameReference extends PsiReferenceBase<XmlAttributeValue> {
|
||||
private final String fieldName;
|
||||
|
||||
public DynFormFormNameReference(@NotNull XmlAttributeValue element, TextRange range, String fieldName) {
|
||||
super(element, range, true);
|
||||
this.fieldName = fieldName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiElement resolve() {
|
||||
return findFieldInFormEntriesRecursive(myElement.getContainingFile(), fieldName, new HashSet<>());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PsiElement findFieldInFormEntriesRecursive(PsiFile file, String name, Set<PsiFile> visited) {
|
||||
if (file == null || !visited.add(file)) return null;
|
||||
if (!(file instanceof XmlFile xmlFile)) return null;
|
||||
XmlTag rootTag = xmlFile.getRootTag();
|
||||
if (rootTag == null) return null;
|
||||
|
||||
// 1. Search in current file FORM_ENTRY tags
|
||||
for (XmlTag formTag : rootTag.findSubTags("FORM")) {
|
||||
for (XmlTag entryTag : formTag.findSubTags("FORM_ENTRY")) {
|
||||
// Check hidden fields in FIELDS
|
||||
XmlTag fieldsTag = entryTag.findFirstSubTag("FIELDS");
|
||||
if (fieldsTag != null) {
|
||||
for (XmlTag fieldTag : fieldsTag.findSubTags("FIELD")) {
|
||||
if ("HIDDEN".equals(fieldTag.getAttributeValue("INPUTTYPE")) && name.equals(fieldTag.getAttributeValue("NAME"))) {
|
||||
XmlAttribute nameAttr = fieldTag.getAttribute("NAME");
|
||||
return nameAttr != null ? nameAttr.getValueElement() : fieldTag;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check fields in LAYOUT
|
||||
XmlTag layoutTag = entryTag.findFirstSubTag("LAYOUT");
|
||||
if (layoutTag != null) {
|
||||
PsiElement found = findFieldInTag(layoutTag, name);
|
||||
if (found != null) return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Search in included files
|
||||
XmlTag includesTag = rootTag.findFirstSubTag("INCLUDES");
|
||||
if (includesTag != null) {
|
||||
for (XmlTag includeTag : includesTag.findSubTags("INCLUDE")) {
|
||||
String path = includeTag.getAttributeValue("FILE");
|
||||
if (path != null) {
|
||||
PsiFile includedFile = DynFormPathUtils.findIncludedFile(file, path);
|
||||
PsiElement found = findFieldInFormEntriesRecursive(includedFile, name, visited);
|
||||
if (found != null) return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,12 @@
|
||||
]]></description>
|
||||
|
||||
<change-notes><![CDATA[
|
||||
<h2>[3.2.5]</h2>
|
||||
<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>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>
|
||||
</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>
|
||||
|
||||
Reference in New Issue
Block a user