feat(dynform): enhance dataset and field referencing for master-detail structures
- Implemented comprehensive reference and completion support for <FOREIGN-DATASETS> and <MASTER-DATA> tags. - Enhanced dataset resolution to support recursive scanning across included .frml files. - Improved field resolution logic for MASTER-FIELDS and DETAIL-FIELDS to resolve from datasets specified by DATASET-ID or DATAID. - Bumped plugin version to 3.2.3 and updated change notes.
This commit is contained in:
@@ -3,12 +3,12 @@ plugins {
|
||||
id("org.jetbrains.kotlin.jvm") version "2.1.0"
|
||||
id("org.jetbrains.intellij.platform") version "2.7.0"
|
||||
}
|
||||
|
||||
group = "com.sdk.dynform.tools"
|
||||
version = "3.2.2"
|
||||
version = "3.2.3"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
||||
intellijPlatform {
|
||||
defaultRepositories()
|
||||
}
|
||||
@@ -39,6 +39,12 @@ intellijPlatform {
|
||||
}
|
||||
|
||||
changeNotes = """
|
||||
<h2>[3.2.3]</h2>
|
||||
<ul>
|
||||
<li><strong>Advanced Data Referencing:</strong> Implemented comprehensive reference and completion support for <code><FOREIGN-DATASETS></code> and <code><MASTER-DATA></code> structures.</li>
|
||||
<li><strong>Contextual Field Resolution:</strong> Enhanced field resolution logic to resolve fields from datasets specified by <code>DATASET-ID</code> and <code>DATAID</code> attributes within their respective tags.</li>
|
||||
<li><strong>Recursive Resource Scanning:</strong> Improved dataset and grid scanning to search across recursively included <code>.frml</code> files, ensuring consistent navigation and completion throughout the project.</li>
|
||||
</ul>
|
||||
<h2>[3.2.2]</h2>
|
||||
<ul>
|
||||
<li><strong>UI/UX Improvement:</strong> Updated the I18n settings to allow selecting the message bundle XML file directly via a file browser, improving configuration usability.</li>
|
||||
|
||||
@@ -102,19 +102,60 @@ public class DynFormCompletionContributor extends CompletionContributor {
|
||||
}
|
||||
});
|
||||
|
||||
// XML completion for Dataset ID
|
||||
// XML completion for Dataset ID (DATAID, DATASET, DATASET-ID, VIEW-DATASET)
|
||||
extend(CompletionType.BASIC, XmlPatterns.psiElement()
|
||||
.inside(XmlPatterns.xmlAttributeValue()
|
||||
.withParent(XmlPatterns.xmlAttribute().withName("DATAID", "DATASET", "DATASET-ID"))),
|
||||
.withParent(XmlPatterns.xmlAttribute().withName("DATAID", "DATASET", "DATASET-ID", "VIEW-DATASET"))),
|
||||
new CompletionProvider<CompletionParameters>() {
|
||||
@Override
|
||||
protected void addCompletions(@NotNull CompletionParameters parameters,
|
||||
@NotNull ProcessingContext context,
|
||||
@NotNull CompletionResultSet resultSet) {
|
||||
addDatasetsInFile(parameters.getOriginalFile(), resultSet);
|
||||
PsiFile ajaxFile = DynFormPathUtils.findAjaxXml(parameters.getOriginalFile());
|
||||
if (ajaxFile != null) {
|
||||
addDatasetsInFile(ajaxFile, resultSet);
|
||||
addDatasetsInFileRecursive(parameters.getOriginalFile(), resultSet, new HashSet<>());
|
||||
}
|
||||
});
|
||||
|
||||
// XML completion for MASTER-FIELDS and DETAIL-FIELDS in MASTER-DATA or FOREIGN-DATASETS > DATASET
|
||||
extend(CompletionType.BASIC, XmlPatterns.psiElement()
|
||||
.inside(XmlPatterns.xmlAttributeValue()
|
||||
.withParent(XmlPatterns.xmlAttribute().withName("MASTER-FIELDS", "DETAIL-FIELDS")
|
||||
.withParent(XmlPatterns.xmlTag().withName("MASTER-DATA", "DATASET")))),
|
||||
new CompletionProvider<CompletionParameters>() {
|
||||
@Override
|
||||
protected void addCompletions(@NotNull CompletionParameters parameters,
|
||||
@NotNull ProcessingContext context,
|
||||
@NotNull CompletionResultSet resultSet) {
|
||||
PsiElement position = parameters.getPosition();
|
||||
XmlAttribute attr = PsiTreeUtil.getParentOfType(position, XmlAttribute.class);
|
||||
if (attr == null) return;
|
||||
XmlTag tag = (XmlTag) attr.getParent();
|
||||
String tagName = tag.getName();
|
||||
String attrName = attr.getName();
|
||||
|
||||
if ("MASTER-DATA".equals(tagName)) {
|
||||
if ("MASTER-FIELDS".equals(attrName)) {
|
||||
addFieldsFromDatasetId(tag, resultSet);
|
||||
} else if ("DETAIL-FIELDS".equals(attrName)) {
|
||||
XmlTag gridTag = tag.getParentTag();
|
||||
if (gridTag != null && "DATA-GRID".equals(gridTag.getName())) {
|
||||
addFieldsFromGrid(gridTag, resultSet);
|
||||
}
|
||||
}
|
||||
} else if ("DATASET".equals(tagName) && hasAncestorWithName(tag, "FOREIGN-DATASETS")) {
|
||||
if ("DETAIL-FIELDS".equals(attrName)) {
|
||||
addFieldsFromDatasetId(tag, resultSet);
|
||||
} else if ("MASTER-FIELDS".equals(attrName)) {
|
||||
XmlTag foreignDatasetsTag = tag.getParentTag();
|
||||
if (foreignDatasetsTag != null) {
|
||||
XmlTag parentDataset = foreignDatasetsTag.getParentTag();
|
||||
if (parentDataset != null && "DATASET".equals(parentDataset.getName())) {
|
||||
XmlTag fieldsTag = parentDataset.findFirstSubTag("FIELDS");
|
||||
if (fieldsTag != null) {
|
||||
addFieldsInTagRecursive(fieldsTag, resultSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -195,6 +236,36 @@ public class DynFormCompletionContributor extends CompletionContributor {
|
||||
}
|
||||
}
|
||||
|
||||
private void addDatasetsInFileRecursive(PsiFile file, @NotNull CompletionResultSet resultSet, Set<PsiFile> visited) {
|
||||
if (file == null || !visited.add(file)) return;
|
||||
|
||||
// 1. Add datasets from current file
|
||||
addDatasetsInFile(file, resultSet);
|
||||
|
||||
// 2. Add datasets from ajax.xml (only if not already added)
|
||||
PsiFile ajaxFile = DynFormPathUtils.findAjaxXml(file);
|
||||
if (ajaxFile != null && ajaxFile != file) {
|
||||
addDatasetsInFile(ajaxFile, resultSet);
|
||||
}
|
||||
|
||||
// 3. Add datasets from included files
|
||||
if (file instanceof XmlFile xmlFile) {
|
||||
XmlTag rootTag = xmlFile.getRootTag();
|
||||
if (rootTag != null) {
|
||||
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);
|
||||
addDatasetsInFileRecursive(includedFile, resultSet, visited);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addDatasetsInFile(PsiFile file, @NotNull CompletionResultSet resultSet) {
|
||||
if (!(file instanceof XmlFile xmlFile)) return;
|
||||
XmlTag rootTag = xmlFile.getRootTag();
|
||||
@@ -242,6 +313,110 @@ public class DynFormCompletionContributor extends CompletionContributor {
|
||||
}
|
||||
}
|
||||
|
||||
private void addFieldsFromDatasetId(XmlTag tag, @NotNull CompletionResultSet resultSet) {
|
||||
String datasetId = tag.getAttributeValue("DATASET-ID");
|
||||
if (datasetId == null || datasetId.isEmpty()) return;
|
||||
|
||||
PsiFile file = tag.getContainingFile();
|
||||
PsiElement datasetElement = findDatasetElement(file, datasetId);
|
||||
if (datasetElement instanceof XmlTag datasetTag) {
|
||||
XmlTag fieldsTag = datasetTag.findFirstSubTag("FIELDS");
|
||||
if (fieldsTag != null) {
|
||||
addFieldsInTagRecursive(fieldsTag, resultSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addFieldsFromGrid(XmlTag gridTag, @NotNull CompletionResultSet resultSet) {
|
||||
// 1. Try to get DATAID from GRID-LIST or GRID-EDITOR
|
||||
String dataId = null;
|
||||
XmlTag listTag = gridTag.findFirstSubTag("GRID-LIST");
|
||||
if (listTag != null) {
|
||||
dataId = listTag.getAttributeValue("DATAID");
|
||||
}
|
||||
if (dataId == null) {
|
||||
XmlTag editorTag = gridTag.findFirstSubTag("GRID-EDITOR");
|
||||
if (editorTag != null) {
|
||||
dataId = editorTag.getAttributeValue("DATAID");
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Add fields from that DATASET
|
||||
if (dataId != null && !dataId.isEmpty()) {
|
||||
PsiElement datasetElement = findDatasetElement(gridTag.getContainingFile(), dataId);
|
||||
if (datasetElement instanceof XmlTag datasetTag) {
|
||||
XmlTag fieldsTag = datasetTag.findFirstSubTag("FIELDS");
|
||||
if (fieldsTag != null) {
|
||||
addFieldsInTagRecursive(fieldsTag, resultSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: Check for inline FIELDS in GRID-LIST/GRID-EDITOR (if any)
|
||||
if (listTag != null) {
|
||||
XmlTag fieldsTag = listTag.findFirstSubTag("FIELDS");
|
||||
if (fieldsTag != null) {
|
||||
addFieldsInTagRecursive(fieldsTag, resultSet);
|
||||
}
|
||||
}
|
||||
XmlTag editorTag = gridTag.findFirstSubTag("GRID-EDITOR");
|
||||
if (editorTag != null) {
|
||||
XmlTag fieldsTag = editorTag.findFirstSubTag("FIELDS");
|
||||
if (fieldsTag != null) {
|
||||
addFieldsInTagRecursive(fieldsTag, resultSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PsiElement findDatasetElement(PsiFile file, String id) {
|
||||
return findDatasetInFileRecursiveForCompletion(file, id, new HashSet<>());
|
||||
}
|
||||
|
||||
private PsiElement findDatasetInFileRecursiveForCompletion(PsiFile file, String id, 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;
|
||||
|
||||
XmlTag datasetsContainer = rootTag.findFirstSubTag("DATASETS");
|
||||
if (datasetsContainer == null) datasetsContainer = rootTag;
|
||||
|
||||
for (XmlTag datasetTag : datasetsContainer.findSubTags("DATASET")) {
|
||||
if (id.equals(datasetTag.getAttributeValue("ID"))) {
|
||||
return datasetTag;
|
||||
}
|
||||
}
|
||||
|
||||
PsiFile ajaxFile = DynFormPathUtils.findAjaxXml(file);
|
||||
if (ajaxFile != null && ajaxFile != file) {
|
||||
PsiElement found = findDatasetInFileRecursiveForCompletion(ajaxFile, id, visited);
|
||||
if (found != null) return found;
|
||||
}
|
||||
|
||||
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 = findDatasetInFileRecursiveForCompletion(includedFile, id, visited);
|
||||
if (found != null) return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private PsiNewExpression getNewDynFormExpression(PsiLiteralExpression literal) {
|
||||
PsiElement parent = literal.getParent();
|
||||
if (parent instanceof PsiExpressionList) {
|
||||
|
||||
@@ -83,9 +83,9 @@ public class DynFormReferenceContributor extends PsiReferenceContributor {
|
||||
}
|
||||
});
|
||||
|
||||
// XML Reference for Dataset ID (DATAID, DATASET, DATASET-ID)
|
||||
// XML Reference for Dataset ID (DATAID, DATASET, DATASET-ID, VIEW-DATASET)
|
||||
registrar.registerReferenceProvider(XmlPatterns.xmlAttributeValue()
|
||||
.withParent(XmlPatterns.xmlAttribute().withName("DATAID", "DATASET", "DATASET-ID")),
|
||||
.withParent(XmlPatterns.xmlAttribute().withName("DATAID", "DATASET", "DATASET-ID", "VIEW-DATASET")),
|
||||
new PsiReferenceProvider() {
|
||||
@NotNull
|
||||
@Override
|
||||
@@ -97,6 +97,41 @@ public class DynFormReferenceContributor extends PsiReferenceContributor {
|
||||
}
|
||||
});
|
||||
|
||||
// XML Reference for MASTER-FIELDS and DETAIL-FIELDS in MASTER-DATA or FOREIGN-DATASETS > DATASET
|
||||
registrar.registerReferenceProvider(XmlPatterns.xmlAttributeValue()
|
||||
.withParent(XmlPatterns.xmlAttribute().withName("MASTER-FIELDS", "DETAIL-FIELDS")
|
||||
.withParent(XmlPatterns.xmlTag().withName("MASTER-DATA", "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;
|
||||
|
||||
XmlAttribute attr = (XmlAttribute) attrValue.getParent();
|
||||
XmlTag tag = (XmlTag) attr.getParent();
|
||||
String tagName = tag.getName();
|
||||
|
||||
// Only process DATASET tag if it's inside FOREIGN-DATASETS
|
||||
if ("DATASET".equals(tagName) && !hasAncestorWithName(tag, "FOREIGN-DATASETS")) {
|
||||
return PsiReference.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
// Support comma-separated fields
|
||||
String[] fields = value.split(",");
|
||||
PsiReference[] refs = new PsiReference[fields.length];
|
||||
int currentOffset = 1;
|
||||
for (int i = 0; i < fields.length; i++) {
|
||||
String field = fields[i].trim();
|
||||
int start = value.indexOf(field, currentOffset - 1);
|
||||
refs[i] = new DynFormMasterDataFieldReference(attrValue, new TextRange(start + 1, start + 1 + field.length()), field);
|
||||
currentOffset = start + field.length();
|
||||
}
|
||||
return refs;
|
||||
}
|
||||
});
|
||||
|
||||
// XML Reference for Grid ID
|
||||
registrar.registerReferenceProvider(XmlPatterns.xmlAttributeValue()
|
||||
.withParent(XmlPatterns.xmlAttribute().withName("GRID-ID")),
|
||||
@@ -324,12 +359,39 @@ public class DynFormReferenceContributor extends PsiReferenceContributor {
|
||||
this.datasetId = datasetId;
|
||||
}
|
||||
@Nullable @Override public PsiElement resolve() {
|
||||
PsiElement found = findDatasetInFile(myElement.getContainingFile(), datasetId);
|
||||
return findDatasetInFileRecursive(myElement.getContainingFile(), datasetId, new HashSet<>());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PsiElement findDatasetInFileRecursive(PsiFile file, String id, Set<PsiFile> visited) {
|
||||
if (file == null || !visited.add(file)) return null;
|
||||
if (!(file instanceof XmlFile xmlFile)) return null;
|
||||
|
||||
// 1. Search in current file
|
||||
PsiElement found = findDatasetInFile(xmlFile, id);
|
||||
if (found != null) return found;
|
||||
|
||||
PsiFile ajaxFile = DynFormPathUtils.findAjaxXml(myElement.getContainingFile());
|
||||
if (ajaxFile != null) {
|
||||
return findDatasetInFile(ajaxFile, datasetId);
|
||||
// 2. Search in ajax.xml (only from main file)
|
||||
PsiFile ajaxFile = DynFormPathUtils.findAjaxXml(file);
|
||||
if (ajaxFile != null && ajaxFile != file) {
|
||||
found = findDatasetInFile(ajaxFile, id);
|
||||
if (found != null) return found;
|
||||
}
|
||||
|
||||
// 3. Search in included files
|
||||
XmlTag rootTag = xmlFile.getRootTag();
|
||||
if (rootTag != null) {
|
||||
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);
|
||||
found = findDatasetInFileRecursive(includedFile, id, visited);
|
||||
if (found != null) return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -353,6 +415,163 @@ public class DynFormReferenceContributor extends PsiReferenceContributor {
|
||||
}
|
||||
}
|
||||
|
||||
private static class DynFormMasterDataFieldReference extends PsiReferenceBase<XmlAttributeValue> {
|
||||
private final String fieldName;
|
||||
|
||||
public DynFormMasterDataFieldReference(@NotNull XmlAttributeValue element, TextRange range, String fieldName) {
|
||||
super(element, range, true);
|
||||
this.fieldName = fieldName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiElement resolve() {
|
||||
XmlAttribute attr = (XmlAttribute) myElement.getParent();
|
||||
XmlTag tag = (XmlTag) attr.getParent();
|
||||
String tagName = tag.getName();
|
||||
String attrName = attr.getName();
|
||||
|
||||
if ("MASTER-DATA".equals(tagName)) {
|
||||
if ("MASTER-FIELDS".equals(attrName)) {
|
||||
// MASTER-FIELDS refers to the DATASET specified in DATASET-ID
|
||||
return resolveInDataset(tag.getAttributeValue("DATASET-ID"));
|
||||
} else if ("DETAIL-FIELDS".equals(attrName)) {
|
||||
// DETAIL-FIELDS refers to fields in current DATA-GRID
|
||||
XmlTag gridTag = tag.getParentTag();
|
||||
if (gridTag != null && "DATA-GRID".equals(gridTag.getName())) {
|
||||
return findFieldInGrid(gridTag);
|
||||
}
|
||||
}
|
||||
} else if ("DATASET".equals(tagName)) {
|
||||
if ("DETAIL-FIELDS".equals(attrName)) {
|
||||
// DETAIL-FIELDS refers to the DATASET specified in DATASET-ID
|
||||
return resolveInDataset(tag.getAttributeValue("DATASET-ID"));
|
||||
} else if ("MASTER-FIELDS".equals(attrName)) {
|
||||
// MASTER-FIELDS refers to parent DATASET
|
||||
XmlTag foreignDatasetsTag = tag.getParentTag();
|
||||
if (foreignDatasetsTag != null) {
|
||||
XmlTag parentDataset = foreignDatasetsTag.getParentTag();
|
||||
if (parentDataset != null && "DATASET".equals(parentDataset.getName())) {
|
||||
XmlTag fieldsTag = parentDataset.findFirstSubTag("FIELDS");
|
||||
if (fieldsTag != null) {
|
||||
return findFieldInTag(fieldsTag, fieldName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PsiElement resolveInDataset(String datasetId) {
|
||||
if (datasetId == null || datasetId.isEmpty()) return null;
|
||||
|
||||
// Re-use DynFormDatasetReference's finding logic if possible, or just look up
|
||||
PsiElement datasetElement = findDatasetElement(datasetId);
|
||||
if (datasetElement instanceof XmlTag datasetTag) {
|
||||
XmlTag fieldsTag = datasetTag.findFirstSubTag("FIELDS");
|
||||
if (fieldsTag != null) {
|
||||
return findFieldInTag(fieldsTag, fieldName);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PsiElement findDatasetElement(String id) {
|
||||
// This is a simplified lookup, similar to DynFormDatasetReference's resolve
|
||||
PsiFile file = myElement.getContainingFile();
|
||||
PsiElement found = findDatasetInFileRecursive(file, id, new HashSet<>());
|
||||
if (found instanceof XmlAttributeValue attrValue) {
|
||||
PsiElement parent = attrValue.getParent();
|
||||
if (parent instanceof XmlAttribute attr) {
|
||||
return attr.getParent();
|
||||
}
|
||||
}
|
||||
if (found instanceof XmlTag) return found;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PsiElement findDatasetInFileRecursive(PsiFile file, String id, 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;
|
||||
|
||||
XmlTag datasetsContainer = rootTag.findFirstSubTag("DATASETS");
|
||||
if (datasetsContainer == null) datasetsContainer = rootTag;
|
||||
|
||||
for (XmlTag datasetTag : datasetsContainer.findSubTags("DATASET")) {
|
||||
if (id.equals(datasetTag.getAttributeValue("ID"))) {
|
||||
XmlAttribute idAttr = datasetTag.getAttribute("ID");
|
||||
return idAttr != null ? idAttr.getValueElement() : datasetTag;
|
||||
}
|
||||
}
|
||||
|
||||
// Search in ajax.xml
|
||||
PsiFile ajaxFile = DynFormPathUtils.findAjaxXml(file);
|
||||
if (ajaxFile != null && ajaxFile != file) {
|
||||
PsiElement found = findDatasetInFileRecursive(ajaxFile, id, visited);
|
||||
if (found != null) return found;
|
||||
}
|
||||
|
||||
// Search in includes
|
||||
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 = findDatasetInFileRecursive(includedFile, id, visited);
|
||||
if (found != null) return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PsiElement findFieldInGrid(XmlTag gridTag) {
|
||||
// 1. Try to get DATAID from GRID-LIST or GRID-EDITOR
|
||||
String dataId = null;
|
||||
XmlTag listTag = gridTag.findFirstSubTag("GRID-LIST");
|
||||
if (listTag != null) {
|
||||
dataId = listTag.getAttributeValue("DATAID");
|
||||
}
|
||||
if (dataId == null) {
|
||||
XmlTag editorTag = gridTag.findFirstSubTag("GRID-EDITOR");
|
||||
if (editorTag != null) {
|
||||
dataId = editorTag.getAttributeValue("DATAID");
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Resolve fields from that DATASET
|
||||
if (dataId != null && !dataId.isEmpty()) {
|
||||
return resolveInDataset(dataId);
|
||||
}
|
||||
|
||||
// Fallback: Check for inline FIELDS in GRID-LIST/GRID-EDITOR (if any)
|
||||
if (listTag != null) {
|
||||
XmlTag fieldsTag = listTag.findFirstSubTag("FIELDS");
|
||||
if (fieldsTag != null) {
|
||||
PsiElement found = findFieldInTag(fieldsTag, fieldName);
|
||||
if (found != null) return found;
|
||||
}
|
||||
}
|
||||
XmlTag editorTag = gridTag.findFirstSubTag("GRID-EDITOR");
|
||||
if (editorTag != null) {
|
||||
XmlTag fieldsTag = editorTag.findFirstSubTag("FIELDS");
|
||||
if (fieldsTag != null) {
|
||||
return findFieldInTag(fieldsTag, fieldName);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class DynFormGridReference extends PsiReferenceBase<XmlAttributeValue> {
|
||||
private final String gridId;
|
||||
public DynFormGridReference(@NotNull XmlAttributeValue element, TextRange range, String gridId) {
|
||||
|
||||
@@ -34,6 +34,12 @@
|
||||
]]></description>
|
||||
|
||||
<change-notes><![CDATA[
|
||||
<h2>[3.2.3]</h2>
|
||||
<ul>
|
||||
<li><strong>Advanced Data Referencing:</strong> Implemented comprehensive reference and completion support for <code><FOREIGN-DATASETS></code> and <code><MASTER-DATA></code> structures.</li>
|
||||
<li><strong>Contextual Field Resolution:</strong> Enhanced field resolution logic to resolve fields from datasets specified by <code>DATASET-ID</code> and <code>DATAID</code> attributes within their respective tags.</li>
|
||||
<li><strong>Recursive Resource Scanning:</strong> Improved dataset and grid scanning to search across recursively included <code>.frml</code> files, ensuring consistent navigation and completion throughout the project.</li>
|
||||
</ul>
|
||||
<h2>[3.2.2]</h2>
|
||||
<ul>
|
||||
<li><strong>UI/UX Improvement:</strong> Updated the I18n settings to allow selecting the message bundle XML file directly via a file browser, improving configuration usability.</li>
|
||||
|
||||
Reference in New Issue
Block a user