Update plugin version to 3.2.8 and enhance DynForm references
- Add cross-file Dataset and Field resolution for included .frml files - Prioritize opened files in editor for reference navigation - Restrict AJAX-OPTION DATASET auto-completion to suggest from ajax.xml - Update change notes for 3.2.8 release
This commit is contained in:
@@ -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.7"
|
version = "3.2.8"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
|||||||
@@ -113,7 +113,18 @@ public class DynFormCompletionContributor extends CompletionContributor {
|
|||||||
protected void addCompletions(@NotNull CompletionParameters parameters,
|
protected void addCompletions(@NotNull CompletionParameters parameters,
|
||||||
@NotNull ProcessingContext context,
|
@NotNull ProcessingContext context,
|
||||||
@NotNull CompletionResultSet resultSet) {
|
@NotNull CompletionResultSet resultSet) {
|
||||||
addDatasetsInFileRecursive(parameters.getOriginalFile(), resultSet, new HashSet<>());
|
PsiElement position = parameters.getPosition();
|
||||||
|
XmlTag parentTag = PsiTreeUtil.getParentOfType(position, XmlTag.class);
|
||||||
|
boolean isAjaxOption = parentTag != null && "AJAX-OPTION".equals(parentTag.getName());
|
||||||
|
|
||||||
|
if (isAjaxOption) {
|
||||||
|
PsiFile ajaxFile = DynFormPathUtils.findAjaxXml(parameters.getOriginalFile());
|
||||||
|
if (ajaxFile != null) {
|
||||||
|
addDatasetsInFile(ajaxFile, resultSet);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addDatasetsInFileRecursive(parameters.getOriginalFile(), resultSet, new HashSet<>(), false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -268,19 +279,32 @@ public class DynFormCompletionContributor extends CompletionContributor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addDatasetsInFileRecursive(PsiFile file, @NotNull CompletionResultSet resultSet, Set<PsiFile> visited) {
|
private void addDatasetsInFileRecursive(PsiFile file, @NotNull CompletionResultSet resultSet, Set<PsiFile> visited, boolean includeAjax) {
|
||||||
if (file == null || !visited.add(file)) return;
|
if (file == null || !visited.add(file)) return;
|
||||||
|
|
||||||
|
if (includeAjax) {
|
||||||
|
// สำหรับ AJAX-OPTION ให้หาใน ajax.xml เท่านั้น
|
||||||
|
PsiFile ajaxFile = DynFormPathUtils.findAjaxXml(file);
|
||||||
|
if (ajaxFile != null) {
|
||||||
|
addDatasetsInFile(ajaxFile, resultSet);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 1. Add datasets from current file
|
// 1. Add datasets from current file
|
||||||
addDatasetsInFile(file, resultSet);
|
addDatasetsInFile(file, resultSet);
|
||||||
|
|
||||||
// 2. Add datasets from ajax.xml (only if not already added)
|
// 2. Add datasets from files that INCLUDE this file (Parent/Main Files)
|
||||||
PsiFile ajaxFile = DynFormPathUtils.findAjaxXml(file);
|
if (visited.size() == 1) {
|
||||||
if (ajaxFile != null && ajaxFile != file) {
|
List<PsiFile> includers = DynFormPathUtils.findIncluders(file);
|
||||||
addDatasetsInFile(ajaxFile, resultSet);
|
for (PsiFile includer : includers) {
|
||||||
|
if (visited.add(includer)) {
|
||||||
|
addDatasetsInFile(includer, resultSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Add datasets from included files
|
// 3. Add datasets from included files (Downward)
|
||||||
if (file instanceof XmlFile xmlFile) {
|
if (file instanceof XmlFile xmlFile) {
|
||||||
XmlTag rootTag = xmlFile.getRootTag();
|
XmlTag rootTag = xmlFile.getRootTag();
|
||||||
if (rootTag != null) {
|
if (rootTag != null) {
|
||||||
@@ -290,12 +314,23 @@ public class DynFormCompletionContributor extends CompletionContributor {
|
|||||||
String path = includeTag.getAttributeValue("FILE");
|
String path = includeTag.getAttributeValue("FILE");
|
||||||
if (path != null) {
|
if (path != null) {
|
||||||
PsiFile includedFile = DynFormPathUtils.findIncludedFile(file, path);
|
PsiFile includedFile = DynFormPathUtils.findIncludedFile(file, path);
|
||||||
addDatasetsInFileRecursive(includedFile, resultSet, visited);
|
if (includedFile != null && !visited.contains(includedFile)) {
|
||||||
|
addDatasetsInFileRecursive(includedFile, resultSet, visited, includeAjax);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4. Add datasets from all .frml files in module (fallback)
|
||||||
|
if (visited.size() <= 2) {
|
||||||
|
for (PsiFile frmlFile : DynFormPathUtils.getAllFrmlFiles(file)) {
|
||||||
|
if (visited.add(frmlFile)) {
|
||||||
|
addDatasetsInFile(frmlFile, resultSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addDatasetsInFile(PsiFile file, @NotNull CompletionResultSet resultSet) {
|
private void addDatasetsInFile(PsiFile file, @NotNull CompletionResultSet resultSet) {
|
||||||
@@ -318,6 +353,50 @@ public class DynFormCompletionContributor extends CompletionContributor {
|
|||||||
|
|
||||||
private void addGridsInFileRecursive(PsiFile file, @NotNull CompletionResultSet resultSet, Set<PsiFile> visited) {
|
private void addGridsInFileRecursive(PsiFile file, @NotNull CompletionResultSet resultSet, Set<PsiFile> visited) {
|
||||||
if (file == null || !visited.add(file)) return;
|
if (file == null || !visited.add(file)) return;
|
||||||
|
|
||||||
|
// 1. Add grids from current file
|
||||||
|
addGridsInFile(file, resultSet);
|
||||||
|
|
||||||
|
// 2. Add grids from files that INCLUDE this file (Parent/Main Files)
|
||||||
|
if (visited.size() == 1) {
|
||||||
|
List<PsiFile> includers = DynFormPathUtils.findIncluders(file);
|
||||||
|
for (PsiFile includer : includers) {
|
||||||
|
if (visited.add(includer)) {
|
||||||
|
addGridsInFile(includer, resultSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Add grids from included files (Downward)
|
||||||
|
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);
|
||||||
|
if (includedFile != null && !visited.contains(includedFile)) {
|
||||||
|
addGridsInFileRecursive(includedFile, resultSet, visited);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Add grids from all .frml files in module (fallback)
|
||||||
|
if (visited.size() <= 2) {
|
||||||
|
for (PsiFile frmlFile : DynFormPathUtils.getAllFrmlFiles(file)) {
|
||||||
|
if (visited.add(frmlFile)) {
|
||||||
|
addGridsInFile(frmlFile, resultSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addGridsInFile(PsiFile file, @NotNull CompletionResultSet resultSet) {
|
||||||
if (!(file instanceof XmlFile xmlFile)) return;
|
if (!(file instanceof XmlFile xmlFile)) return;
|
||||||
XmlTag rootTag = xmlFile.getRootTag();
|
XmlTag rootTag = xmlFile.getRootTag();
|
||||||
if (rootTag == null) return;
|
if (rootTag == null) return;
|
||||||
@@ -332,17 +411,6 @@ public class DynFormCompletionContributor extends CompletionContributor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
addGridsInFileRecursive(includedFile, resultSet, visited);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addFieldsFromDatasetId(XmlTag tag, @NotNull CompletionResultSet resultSet) {
|
private void addFieldsFromDatasetId(XmlTag tag, @NotNull CompletionResultSet resultSet) {
|
||||||
@@ -401,13 +469,34 @@ public class DynFormCompletionContributor extends CompletionContributor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private PsiElement findDatasetElement(PsiFile file, String id) {
|
private PsiElement findDatasetElement(PsiFile file, String id) {
|
||||||
return findDatasetInFileRecursiveForCompletion(file, id, new HashSet<>());
|
Set<PsiFile> visited = new HashSet<>();
|
||||||
|
|
||||||
|
// 1. Local
|
||||||
|
PsiElement found = findDatasetInFile(file, id);
|
||||||
|
if (found != null) return found;
|
||||||
|
visited.add(file);
|
||||||
|
|
||||||
|
// 2. Includers
|
||||||
|
List<PsiFile> includers = DynFormPathUtils.findIncluders(file);
|
||||||
|
for (PsiFile includer : includers) {
|
||||||
|
found = findDatasetInFile(includer, id);
|
||||||
|
if (found != null) return found;
|
||||||
|
visited.add(includer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Ajax
|
||||||
|
PsiFile ajaxXml = DynFormPathUtils.findAjaxXml(file);
|
||||||
|
if (ajaxXml != null && visited.add(ajaxXml)) {
|
||||||
|
found = findDatasetInFile(ajaxXml, id);
|
||||||
|
if (found != null) return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Recursive search
|
||||||
|
return findDatasetInFileRecursiveForCompletion(file, id, visited);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PsiElement findDatasetInFileRecursiveForCompletion(PsiFile file, String id, Set<PsiFile> visited) {
|
private PsiElement findDatasetInFile(PsiFile file, String id) {
|
||||||
if (file == null || !visited.add(file)) return null;
|
|
||||||
if (!(file instanceof XmlFile xmlFile)) return null;
|
if (!(file instanceof XmlFile xmlFile)) return null;
|
||||||
|
|
||||||
XmlTag rootTag = xmlFile.getRootTag();
|
XmlTag rootTag = xmlFile.getRootTag();
|
||||||
if (rootTag == null) return null;
|
if (rootTag == null) return null;
|
||||||
|
|
||||||
@@ -419,24 +508,43 @@ public class DynFormCompletionContributor extends CompletionContributor {
|
|||||||
return datasetTag;
|
return datasetTag;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
PsiFile ajaxFile = DynFormPathUtils.findAjaxXml(file);
|
private PsiElement findDatasetInFileRecursiveForCompletion(PsiFile file, String id, Set<PsiFile> visited) {
|
||||||
if (ajaxFile != null && ajaxFile != file) {
|
if (file == null || !visited.add(file)) return null;
|
||||||
PsiElement found = findDatasetInFileRecursiveForCompletion(ajaxFile, id, visited);
|
if (!(file instanceof XmlFile xmlFile)) return null;
|
||||||
if (found != null) return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Try local (should already be checked by findDatasetElement but for recursion consistency)
|
||||||
|
PsiElement found = findDatasetInFile(xmlFile, id);
|
||||||
|
if (found != null) return found;
|
||||||
|
|
||||||
|
XmlTag rootTag = xmlFile.getRootTag();
|
||||||
|
if (rootTag == null) return null;
|
||||||
|
|
||||||
|
// Downward search
|
||||||
XmlTag includesTag = rootTag.findFirstSubTag("INCLUDES");
|
XmlTag includesTag = rootTag.findFirstSubTag("INCLUDES");
|
||||||
if (includesTag != null) {
|
if (includesTag != null) {
|
||||||
for (XmlTag includeTag : includesTag.findSubTags("INCLUDE")) {
|
for (XmlTag includeTag : includesTag.findSubTags("INCLUDE")) {
|
||||||
String path = includeTag.getAttributeValue("FILE");
|
String path = includeTag.getAttributeValue("FILE");
|
||||||
if (path != null) {
|
if (path != null) {
|
||||||
PsiFile includedFile = DynFormPathUtils.findIncludedFile(file, path);
|
PsiFile includedFile = DynFormPathUtils.findIncludedFile(file, path);
|
||||||
PsiElement found = findDatasetInFileRecursiveForCompletion(includedFile, id, visited);
|
found = findDatasetInFileRecursiveForCompletion(includedFile, id, visited);
|
||||||
if (found != null) return found;
|
if (found != null) return found;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Final fallback: Module-wide
|
||||||
|
if (visited.size() <= 2) {
|
||||||
|
for (PsiFile frmlFile : DynFormPathUtils.getAllFrmlFiles(file)) {
|
||||||
|
if (!visited.contains(frmlFile)) {
|
||||||
|
found = findDatasetInFile(frmlFile, id);
|
||||||
|
if (found != null) return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import com.intellij.openapi.vfs.VirtualFile;
|
|||||||
import com.intellij.psi.PsiDirectory;
|
import com.intellij.psi.PsiDirectory;
|
||||||
import com.intellij.psi.PsiFile;
|
import com.intellij.psi.PsiFile;
|
||||||
import com.intellij.psi.PsiManager;
|
import com.intellij.psi.PsiManager;
|
||||||
|
import com.intellij.psi.xml.XmlFile;
|
||||||
|
import com.intellij.psi.xml.XmlTag;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@@ -140,4 +142,62 @@ public class DynFormPathUtils {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static List<PsiFile> getAllFrmlFiles(@NotNull PsiFile contextFile) {
|
||||||
|
List<PsiFile> files = new ArrayList<>();
|
||||||
|
VirtualFile moduleDir = findModuleDir(contextFile);
|
||||||
|
if (moduleDir != null) {
|
||||||
|
VirtualFile frmDir = moduleDir.findFileByRelativePath("view/frm");
|
||||||
|
if (frmDir != null) {
|
||||||
|
collectFrmlFilesRecursive(frmDir, contextFile.getProject(), files);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static List<PsiFile> findIncluders(@NotNull PsiFile includedFile) {
|
||||||
|
List<PsiFile> includers = new ArrayList<>();
|
||||||
|
Project project = includedFile.getProject();
|
||||||
|
VirtualFile includedVFile = includedFile.getVirtualFile();
|
||||||
|
if (includedVFile == null) return includers;
|
||||||
|
|
||||||
|
VirtualFile moduleDir = findModuleDir(includedFile);
|
||||||
|
if (moduleDir == null) return includers;
|
||||||
|
|
||||||
|
List<PsiFile> allFiles = getAllFrmlFiles(includedFile);
|
||||||
|
for (PsiFile file : allFiles) {
|
||||||
|
if (file.equals(includedFile)) continue;
|
||||||
|
if (!(file instanceof XmlFile xmlFile)) continue;
|
||||||
|
|
||||||
|
XmlTag rootTag = xmlFile.getRootTag();
|
||||||
|
if (rootTag == null) return includers;
|
||||||
|
|
||||||
|
XmlTag includesTag = rootTag.findFirstSubTag("INCLUDES");
|
||||||
|
if (includesTag != null) {
|
||||||
|
for (XmlTag includeTag : includesTag.findSubTags("INCLUDE")) {
|
||||||
|
String path = includeTag.getAttributeValue("FILE");
|
||||||
|
if (path != null) {
|
||||||
|
PsiFile resolved = findIncludedFile(file, path);
|
||||||
|
if (resolved != null && resolved.getVirtualFile().equals(includedVFile)) {
|
||||||
|
includers.add(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return includers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void collectFrmlFilesRecursive(VirtualFile dir, Project project, List<PsiFile> files) {
|
||||||
|
for (VirtualFile child : dir.getChildren()) {
|
||||||
|
if (child.isDirectory()) {
|
||||||
|
collectFrmlFilesRecursive(child, project, files);
|
||||||
|
} else if (child.getName().endsWith(".frml")) {
|
||||||
|
PsiFile psiFile = PsiManager.getInstance(project).findFile(child);
|
||||||
|
if (psiFile != null) files.add(psiFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.sdk.dynform.tools.dynform;
|
package com.sdk.dynform.tools.dynform;
|
||||||
|
|
||||||
|
import com.intellij.openapi.project.Project;
|
||||||
|
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||||
import com.intellij.openapi.util.TextRange;
|
import com.intellij.openapi.util.TextRange;
|
||||||
import com.intellij.patterns.PlatformPatterns;
|
import com.intellij.patterns.PlatformPatterns;
|
||||||
import com.intellij.patterns.XmlPatterns;
|
import com.intellij.patterns.XmlPatterns;
|
||||||
@@ -13,7 +15,9 @@ import com.intellij.util.ProcessingContext;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class DynFormReferenceContributor extends PsiReferenceContributor {
|
public class DynFormReferenceContributor extends PsiReferenceContributor {
|
||||||
@@ -93,7 +97,12 @@ public class DynFormReferenceContributor extends PsiReferenceContributor {
|
|||||||
XmlAttributeValue attrValue = (XmlAttributeValue) element;
|
XmlAttributeValue attrValue = (XmlAttributeValue) element;
|
||||||
String value = attrValue.getValue();
|
String value = attrValue.getValue();
|
||||||
if (value == null || value.isEmpty()) return PsiReference.EMPTY_ARRAY;
|
if (value == null || value.isEmpty()) return PsiReference.EMPTY_ARRAY;
|
||||||
return new PsiReference[]{new DynFormDatasetReference(attrValue, new TextRange(1, value.length() + 1), value)};
|
|
||||||
|
XmlAttribute attr = (XmlAttribute) attrValue.getParent();
|
||||||
|
XmlTag parentTag = attr != null ? (XmlTag) attr.getParent() : null;
|
||||||
|
boolean isAjaxOption = parentTag != null && "AJAX-OPTION".equals(parentTag.getName());
|
||||||
|
|
||||||
|
return new PsiReference[]{new DynFormDatasetReference(attrValue, new TextRange(1, value.length() + 1), value, isAjaxOption)};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -369,55 +378,59 @@ public class DynFormReferenceContributor extends PsiReferenceContributor {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class DynFormDatasetReference extends PsiReferenceBase<XmlAttributeValue> {
|
private static class DynFormDatasetReference extends PsiReferenceBase<XmlAttributeValue> implements PsiPolyVariantReference {
|
||||||
private final String datasetId;
|
private final String datasetId;
|
||||||
public DynFormDatasetReference(@NotNull XmlAttributeValue element, TextRange range, String datasetId) {
|
private final boolean isAjaxOption;
|
||||||
|
|
||||||
|
public DynFormDatasetReference(@NotNull XmlAttributeValue element, TextRange range, String datasetId, boolean isAjaxOption) {
|
||||||
super(element, range, true);
|
super(element, range, true);
|
||||||
this.datasetId = datasetId;
|
this.datasetId = datasetId;
|
||||||
}
|
this.isAjaxOption = isAjaxOption;
|
||||||
@Nullable @Override public PsiElement resolve() {
|
|
||||||
return findDatasetInFileRecursive(myElement.getContainingFile(), datasetId, new HashSet<>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Override
|
||||||
private PsiElement findDatasetInFileRecursive(PsiFile file, String id, Set<PsiFile> visited) {
|
public ResolveResult @NotNull [] multiResolve(boolean incompleteCode) {
|
||||||
if (file == null || !visited.add(file)) return null;
|
List<ResolveResult> results = new ArrayList<>();
|
||||||
if (!(file instanceof XmlFile xmlFile)) return null;
|
PsiFile currentFile = myElement.getContainingFile();
|
||||||
|
|
||||||
// 1. Search in current file
|
if (isAjaxOption) {
|
||||||
PsiElement found = findDatasetInFile(xmlFile, id);
|
// สำหรับ AJAX-OPTION ให้หาใน ajax.xml เท่านั้น
|
||||||
if (found != null) return found;
|
PsiFile ajaxXml = DynFormPathUtils.findAjaxXml(currentFile);
|
||||||
|
if (ajaxXml != null) {
|
||||||
// 2. Search in ajax.xml (only from main file)
|
findDatasetInFile(ajaxXml, datasetId, results);
|
||||||
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 filterOpenFiles(results, myElement.getProject());
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
|
// 1. ค้นหาในไฟล์ปัจจุบัน
|
||||||
|
findDatasetInFile(currentFile, datasetId, results);
|
||||||
|
if (!results.isEmpty()) return filterOpenFiles(results, myElement.getProject());
|
||||||
|
|
||||||
|
// 2. ค้นหาในไฟล์ที่ INCLUDE ไฟล์นี้ (Parent/Main Files)
|
||||||
|
List<PsiFile> includers = DynFormPathUtils.findIncluders(currentFile);
|
||||||
|
for (PsiFile includer : includers) {
|
||||||
|
findDatasetInFile(includer, datasetId, results);
|
||||||
|
}
|
||||||
|
if (!results.isEmpty()) return filterOpenFiles(results, myElement.getProject());
|
||||||
|
|
||||||
|
// 3. ค้นหาในไฟล์ที่ถูก INCLUDE (Downward)
|
||||||
|
findDatasetInIncludesRecursive(currentFile, datasetId, results, new HashSet<>());
|
||||||
|
if (!results.isEmpty()) return filterOpenFiles(results, myElement.getProject());
|
||||||
|
|
||||||
|
// 4. ค้นหาแบบ Module-wide (fallback สุดท้าย)
|
||||||
|
List<PsiFile> allFiles = DynFormPathUtils.getAllFrmlFiles(currentFile);
|
||||||
|
for (PsiFile file : allFiles) {
|
||||||
|
if (file.equals(currentFile) || includers.contains(file)) continue;
|
||||||
|
findDatasetInFile(file, datasetId, results);
|
||||||
|
}
|
||||||
|
|
||||||
|
return filterOpenFiles(results, myElement.getProject());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private void findDatasetInFile(PsiFile file, String id, List<ResolveResult> results) {
|
||||||
private PsiElement findDatasetInFile(PsiFile file, String id) {
|
if (!(file instanceof XmlFile xmlFile)) return;
|
||||||
if (!(file instanceof XmlFile xmlFile)) return null;
|
|
||||||
XmlTag rootTag = xmlFile.getRootTag();
|
XmlTag rootTag = xmlFile.getRootTag();
|
||||||
if (rootTag == null) return null;
|
if (rootTag == null) return;
|
||||||
|
|
||||||
XmlTag datasetsContainer = rootTag.findFirstSubTag("DATASETS");
|
XmlTag datasetsContainer = rootTag.findFirstSubTag("DATASETS");
|
||||||
if (datasetsContainer == null) datasetsContainer = rootTag;
|
if (datasetsContainer == null) datasetsContainer = rootTag;
|
||||||
@@ -425,14 +438,48 @@ public class DynFormReferenceContributor extends PsiReferenceContributor {
|
|||||||
for (XmlTag datasetTag : datasetsContainer.findSubTags("DATASET")) {
|
for (XmlTag datasetTag : datasetsContainer.findSubTags("DATASET")) {
|
||||||
if (id.equals(datasetTag.getAttributeValue("ID"))) {
|
if (id.equals(datasetTag.getAttributeValue("ID"))) {
|
||||||
XmlAttribute idAttr = datasetTag.getAttribute("ID");
|
XmlAttribute idAttr = datasetTag.getAttribute("ID");
|
||||||
return idAttr != null ? idAttr.getValueElement() : datasetTag;
|
results.add(new PsiElementResolveResult(idAttr != null ? idAttr.getValueElement() : datasetTag));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
}
|
||||||
|
|
||||||
|
private void findDatasetInIncludesRecursive(PsiFile file, String id, List<ResolveResult> results, Set<PsiFile> visited) {
|
||||||
|
if (file == null || !visited.add(file) || !(file instanceof XmlFile xmlFile)) return;
|
||||||
|
|
||||||
|
XmlTag rootTag = xmlFile.getRootTag();
|
||||||
|
if (rootTag == null) return;
|
||||||
|
|
||||||
|
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);
|
||||||
|
if (includedFile != null) {
|
||||||
|
findDatasetInFile(includedFile, id, results);
|
||||||
|
if (results.isEmpty()) {
|
||||||
|
findDatasetInIncludesRecursive(includedFile, id, results, visited);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public PsiElement resolve() {
|
||||||
|
ResolveResult[] results = multiResolve(false);
|
||||||
|
return results.length == 1 ? results[0].getElement() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object @NotNull [] getVariants() {
|
||||||
|
return new Object[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class DynFormMasterDataFieldReference extends PsiReferenceBase<XmlAttributeValue> {
|
private static class DynFormMasterDataFieldReference extends PsiReferenceBase<XmlAttributeValue> implements PsiPolyVariantReference {
|
||||||
private final String fieldName;
|
private final String fieldName;
|
||||||
|
|
||||||
public DynFormMasterDataFieldReference(@NotNull XmlAttributeValue element, TextRange range, String fieldName) {
|
public DynFormMasterDataFieldReference(@NotNull XmlAttributeValue element, TextRange range, String fieldName) {
|
||||||
@@ -440,9 +487,9 @@ public class DynFormReferenceContributor extends PsiReferenceContributor {
|
|||||||
this.fieldName = fieldName;
|
this.fieldName = fieldName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
@Override
|
||||||
public PsiElement resolve() {
|
public ResolveResult @NotNull [] multiResolve(boolean incompleteCode) {
|
||||||
|
List<ResolveResult> results = new ArrayList<>();
|
||||||
XmlAttribute attr = (XmlAttribute) myElement.getParent();
|
XmlAttribute attr = (XmlAttribute) myElement.getParent();
|
||||||
XmlTag tag = (XmlTag) attr.getParent();
|
XmlTag tag = (XmlTag) attr.getParent();
|
||||||
String tagName = tag.getName();
|
String tagName = tag.getName();
|
||||||
@@ -450,73 +497,96 @@ public class DynFormReferenceContributor extends PsiReferenceContributor {
|
|||||||
|
|
||||||
if ("MASTER-DATA".equals(tagName)) {
|
if ("MASTER-DATA".equals(tagName)) {
|
||||||
if ("MASTER-FIELDS".equals(attrName)) {
|
if ("MASTER-FIELDS".equals(attrName)) {
|
||||||
// MASTER-FIELDS refers to the DATASET specified in DATASET-ID
|
resolveInDataset(tag.getAttributeValue("DATASET-ID"), results);
|
||||||
return resolveInDataset(tag.getAttributeValue("DATASET-ID"));
|
|
||||||
} else if ("DETAIL-FIELDS".equals(attrName)) {
|
} else if ("DETAIL-FIELDS".equals(attrName)) {
|
||||||
// DETAIL-FIELDS refers to fields in current DATA-GRID
|
|
||||||
XmlTag gridTag = tag.getParentTag();
|
XmlTag gridTag = tag.getParentTag();
|
||||||
if (gridTag != null && "DATA-GRID".equals(gridTag.getName())) {
|
if (gridTag != null && "DATA-GRID".equals(gridTag.getName())) {
|
||||||
return findFieldInGrid(gridTag);
|
PsiElement found = findFieldInGrid(gridTag);
|
||||||
|
if (found != null) results.add(new PsiElementResolveResult(found));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ("DATASET".equals(tagName)) {
|
} else if ("DATASET".equals(tagName)) {
|
||||||
if ("DETAIL-FIELDS".equals(attrName)) {
|
if ("DETAIL-FIELDS".equals(attrName)) {
|
||||||
// DETAIL-FIELDS refers to the DATASET specified in DATASET-ID
|
resolveInDataset(tag.getAttributeValue("DATASET-ID"), results);
|
||||||
return resolveInDataset(tag.getAttributeValue("DATASET-ID"));
|
|
||||||
} else if ("MASTER-FIELDS".equals(attrName)) {
|
} else if ("MASTER-FIELDS".equals(attrName)) {
|
||||||
// MASTER-FIELDS refers to parent DATASET
|
|
||||||
XmlTag foreignDatasetsTag = tag.getParentTag();
|
XmlTag foreignDatasetsTag = tag.getParentTag();
|
||||||
if (foreignDatasetsTag != null) {
|
if (foreignDatasetsTag != null) {
|
||||||
XmlTag parentDataset = foreignDatasetsTag.getParentTag();
|
XmlTag parentDataset = foreignDatasetsTag.getParentTag();
|
||||||
if (parentDataset != null && "DATASET".equals(parentDataset.getName())) {
|
if (parentDataset != null && "DATASET".equals(parentDataset.getName())) {
|
||||||
XmlTag fieldsTag = parentDataset.findFirstSubTag("FIELDS");
|
XmlTag fieldsTag = parentDataset.findFirstSubTag("FIELDS");
|
||||||
if (fieldsTag != null) {
|
if (fieldsTag != null) {
|
||||||
return findFieldInTag(fieldsTag, fieldName);
|
PsiElement found = findFieldInTag(fieldsTag, fieldName);
|
||||||
|
if (found != null) results.add(new PsiElementResolveResult(found));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return filterOpenFiles(results, myElement.getProject());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private PsiElement resolveInDataset(String datasetId) {
|
@Override
|
||||||
if (datasetId == null || datasetId.isEmpty()) return null;
|
public PsiElement resolve() {
|
||||||
|
ResolveResult[] results = multiResolve(false);
|
||||||
|
return results.length == 1 ? results[0].getElement() : null;
|
||||||
|
}
|
||||||
|
|
||||||
// Re-use DynFormDatasetReference's finding logic if possible, or just look up
|
private void resolveInDataset(String datasetId, List<ResolveResult> results) {
|
||||||
PsiElement datasetElement = findDatasetElement(datasetId);
|
if (datasetId == null || datasetId.isEmpty()) return;
|
||||||
if (datasetElement instanceof XmlTag datasetTag) {
|
|
||||||
XmlTag fieldsTag = datasetTag.findFirstSubTag("FIELDS");
|
List<PsiElement> datasetElements = findDatasetElements(datasetId);
|
||||||
if (fieldsTag != null) {
|
for (PsiElement datasetElement : datasetElements) {
|
||||||
return findFieldInTag(fieldsTag, fieldName);
|
XmlTag datasetTag = null;
|
||||||
|
if (datasetElement instanceof XmlTag) datasetTag = (XmlTag) datasetElement;
|
||||||
|
else if (datasetElement instanceof XmlAttributeValue) {
|
||||||
|
XmlAttribute attr = (XmlAttribute) datasetElement.getParent();
|
||||||
|
datasetTag = attr.getParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (datasetTag != null) {
|
||||||
|
XmlTag fieldsTag = datasetTag.findFirstSubTag("FIELDS");
|
||||||
|
if (fieldsTag != null) {
|
||||||
|
PsiElement found = findFieldInTag(fieldsTag, fieldName);
|
||||||
|
if (found != null) results.add(new PsiElementResolveResult(found));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private List<PsiElement> findDatasetElements(String id) {
|
||||||
private PsiElement findDatasetElement(String id) {
|
List<PsiElement> elements = new ArrayList<>();
|
||||||
// This is a simplified lookup, similar to DynFormDatasetReference's resolve
|
PsiFile currentFile = myElement.getContainingFile();
|
||||||
PsiFile file = myElement.getContainingFile();
|
|
||||||
PsiElement found = findDatasetInFileRecursive(file, id, new HashSet<>());
|
// ใช้ Logic เดียวกับ DynFormDatasetReference เพื่อความถูกต้อง
|
||||||
if (found instanceof XmlAttributeValue attrValue) {
|
List<ResolveResult> results = new ArrayList<>();
|
||||||
PsiElement parent = attrValue.getParent();
|
|
||||||
if (parent instanceof XmlAttribute attr) {
|
// 1. Local
|
||||||
return attr.getParent();
|
findDatasetInFile(currentFile, id, results);
|
||||||
|
|
||||||
|
// 2. Includers
|
||||||
|
if (results.isEmpty()) {
|
||||||
|
for (PsiFile includer : DynFormPathUtils.findIncluders(currentFile)) {
|
||||||
|
findDatasetInFile(includer, id, results);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (found instanceof XmlTag) return found;
|
|
||||||
return null;
|
// 3. Ajax
|
||||||
|
if (results.isEmpty()) {
|
||||||
|
PsiFile ajaxXml = DynFormPathUtils.findAjaxXml(currentFile);
|
||||||
|
if (ajaxXml != null) findDatasetInFile(ajaxXml, id, results);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ResolveResult res : results) {
|
||||||
|
elements.add(res.getElement());
|
||||||
|
}
|
||||||
|
return elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private void findDatasetInFile(PsiFile file, String id, List<ResolveResult> results) {
|
||||||
private PsiElement findDatasetInFileRecursive(PsiFile file, String id, Set<PsiFile> visited) {
|
if (!(file instanceof XmlFile xmlFile)) return;
|
||||||
if (file == null || !visited.add(file)) return null;
|
|
||||||
if (!(file instanceof XmlFile xmlFile)) return null;
|
|
||||||
|
|
||||||
XmlTag rootTag = xmlFile.getRootTag();
|
XmlTag rootTag = xmlFile.getRootTag();
|
||||||
if (rootTag == null) return null;
|
if (rootTag == null) return;
|
||||||
|
|
||||||
XmlTag datasetsContainer = rootTag.findFirstSubTag("DATASETS");
|
XmlTag datasetsContainer = rootTag.findFirstSubTag("DATASETS");
|
||||||
if (datasetsContainer == null) datasetsContainer = rootTag;
|
if (datasetsContainer == null) datasetsContainer = rootTag;
|
||||||
@@ -524,97 +594,94 @@ public class DynFormReferenceContributor extends PsiReferenceContributor {
|
|||||||
for (XmlTag datasetTag : datasetsContainer.findSubTags("DATASET")) {
|
for (XmlTag datasetTag : datasetsContainer.findSubTags("DATASET")) {
|
||||||
if (id.equals(datasetTag.getAttributeValue("ID"))) {
|
if (id.equals(datasetTag.getAttributeValue("ID"))) {
|
||||||
XmlAttribute idAttr = datasetTag.getAttribute("ID");
|
XmlAttribute idAttr = datasetTag.getAttribute("ID");
|
||||||
return idAttr != null ? idAttr.getValueElement() : datasetTag;
|
results.add(new PsiElementResolveResult(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
|
@Nullable
|
||||||
private PsiElement findFieldInGrid(XmlTag gridTag) {
|
private PsiElement findFieldInGrid(XmlTag gridTag) {
|
||||||
// 1. Try to get DATAID from GRID-LIST or GRID-EDITOR
|
|
||||||
String dataId = null;
|
String dataId = null;
|
||||||
XmlTag listTag = gridTag.findFirstSubTag("GRID-LIST");
|
XmlTag listTag = gridTag.findFirstSubTag("GRID-LIST");
|
||||||
if (listTag != null) {
|
if (listTag != null) dataId = listTag.getAttributeValue("DATAID");
|
||||||
dataId = listTag.getAttributeValue("DATAID");
|
|
||||||
}
|
|
||||||
if (dataId == null) {
|
if (dataId == null) {
|
||||||
XmlTag editorTag = gridTag.findFirstSubTag("GRID-EDITOR");
|
XmlTag editorTag = gridTag.findFirstSubTag("GRID-EDITOR");
|
||||||
if (editorTag != null) {
|
if (editorTag != null) dataId = editorTag.getAttributeValue("DATAID");
|
||||||
dataId = editorTag.getAttributeValue("DATAID");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Resolve fields from that DATASET
|
|
||||||
if (dataId != null && !dataId.isEmpty()) {
|
if (dataId != null && !dataId.isEmpty()) {
|
||||||
return resolveInDataset(dataId);
|
List<ResolveResult> results = new ArrayList<>();
|
||||||
}
|
resolveInDataset(dataId, results);
|
||||||
|
return !results.isEmpty() ? results.get(0).getElement() : null;
|
||||||
// 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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object @NotNull [] getVariants() {
|
||||||
|
return new Object[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class DynFormGridReference extends PsiReferenceBase<XmlAttributeValue> {
|
private static class DynFormGridReference extends PsiReferenceBase<XmlAttributeValue> implements PsiPolyVariantReference {
|
||||||
private final String gridId;
|
private final String gridId;
|
||||||
|
|
||||||
public DynFormGridReference(@NotNull XmlAttributeValue element, TextRange range, String gridId) {
|
public DynFormGridReference(@NotNull XmlAttributeValue element, TextRange range, String gridId) {
|
||||||
super(element, range, true);
|
super(element, range, true);
|
||||||
this.gridId = gridId;
|
this.gridId = gridId;
|
||||||
}
|
}
|
||||||
@Nullable @Override public PsiElement resolve() {
|
|
||||||
return findGridInFileRecursive(myElement.getContainingFile(), gridId, new HashSet<>());
|
@Override
|
||||||
|
public ResolveResult @NotNull [] multiResolve(boolean incompleteCode) {
|
||||||
|
List<ResolveResult> results = new ArrayList<>();
|
||||||
|
PsiFile currentFile = myElement.getContainingFile();
|
||||||
|
|
||||||
|
// 1. ค้นหาในไฟล์ปัจจุบัน
|
||||||
|
findGridInFile(currentFile, gridId, results);
|
||||||
|
if (!results.isEmpty()) return filterOpenFiles(results, myElement.getProject());
|
||||||
|
|
||||||
|
// 2. ค้นหาในไฟล์ที่ INCLUDE ไฟล์นี้ (Parent/Main Files)
|
||||||
|
List<PsiFile> includers = DynFormPathUtils.findIncluders(currentFile);
|
||||||
|
for (PsiFile includer : includers) {
|
||||||
|
findGridInFile(includer, gridId, results);
|
||||||
|
}
|
||||||
|
if (!results.isEmpty()) return filterOpenFiles(results, myElement.getProject());
|
||||||
|
|
||||||
|
// 3. ค้นหาในไฟล์ที่ถูก INCLUDE (Downward)
|
||||||
|
findGridInIncludesRecursive(currentFile, gridId, results, new HashSet<>());
|
||||||
|
if (!results.isEmpty()) return filterOpenFiles(results, myElement.getProject());
|
||||||
|
|
||||||
|
// 4. ค้นหาแบบ Module-wide (fallback)
|
||||||
|
List<PsiFile> allFiles = DynFormPathUtils.getAllFrmlFiles(currentFile);
|
||||||
|
for (PsiFile file : allFiles) {
|
||||||
|
if (file.equals(currentFile) || includers.contains(file)) continue;
|
||||||
|
findGridInFile(file, gridId, results);
|
||||||
|
}
|
||||||
|
|
||||||
|
return filterOpenFiles(results, myElement.getProject());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private void findGridInFile(PsiFile file, String id, List<ResolveResult> results) {
|
||||||
private PsiElement findGridInFileRecursive(PsiFile file, String id, Set<PsiFile> visited) {
|
if (!(file instanceof XmlFile xmlFile)) return;
|
||||||
if (file == null || !visited.add(file)) return null;
|
|
||||||
if (!(file instanceof XmlFile xmlFile)) return null;
|
|
||||||
XmlTag rootTag = xmlFile.getRootTag();
|
XmlTag rootTag = xmlFile.getRootTag();
|
||||||
if (rootTag == null) return null;
|
if (rootTag == null) return;
|
||||||
|
|
||||||
XmlTag dataGridsTag = rootTag.findFirstSubTag("DATA-GRIDS");
|
XmlTag dataGridsTag = rootTag.findFirstSubTag("DATA-GRIDS");
|
||||||
if (dataGridsTag != null) {
|
if (dataGridsTag != null) {
|
||||||
for (XmlTag gridTag : dataGridsTag.findSubTags("DATA-GRID")) {
|
for (XmlTag gridTag : dataGridsTag.findSubTags("DATA-GRID")) {
|
||||||
if (id.equals(gridTag.getAttributeValue("ID"))) {
|
if (id.equals(gridTag.getAttributeValue("ID"))) {
|
||||||
XmlAttribute idAttr = gridTag.getAttribute("ID");
|
XmlAttribute idAttr = gridTag.getAttribute("ID");
|
||||||
return idAttr != null ? idAttr.getValueElement() : gridTag;
|
results.add(new PsiElementResolveResult(idAttr != null ? idAttr.getValueElement() : gridTag));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void findGridInIncludesRecursive(PsiFile file, String id, List<ResolveResult> results, Set<PsiFile> visited) {
|
||||||
|
if (file == null || !visited.add(file) || !(file instanceof XmlFile xmlFile)) return;
|
||||||
|
|
||||||
|
XmlTag rootTag = xmlFile.getRootTag();
|
||||||
|
if (rootTag == null) return;
|
||||||
|
|
||||||
XmlTag includesTag = rootTag.findFirstSubTag("INCLUDES");
|
XmlTag includesTag = rootTag.findFirstSubTag("INCLUDES");
|
||||||
if (includesTag != null) {
|
if (includesTag != null) {
|
||||||
@@ -622,12 +689,27 @@ public class DynFormReferenceContributor extends PsiReferenceContributor {
|
|||||||
String path = includeTag.getAttributeValue("FILE");
|
String path = includeTag.getAttributeValue("FILE");
|
||||||
if (path != null) {
|
if (path != null) {
|
||||||
PsiFile includedFile = DynFormPathUtils.findIncludedFile(file, path);
|
PsiFile includedFile = DynFormPathUtils.findIncludedFile(file, path);
|
||||||
PsiElement found = findGridInFileRecursive(includedFile, id, visited);
|
if (includedFile != null) {
|
||||||
if (found != null) return found;
|
findGridInFile(includedFile, id, results);
|
||||||
|
if (results.isEmpty()) {
|
||||||
|
findGridInIncludesRecursive(includedFile, id, results, visited);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public PsiElement resolve() {
|
||||||
|
ResolveResult[] results = multiResolve(false);
|
||||||
|
return results.length == 1 ? results[0].getElement() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object @NotNull [] getVariants() {
|
||||||
|
return new Object[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -738,4 +820,23 @@ public class DynFormReferenceContributor extends PsiReferenceContributor {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ResolveResult[] filterOpenFiles(List<ResolveResult> results, Project project) {
|
||||||
|
if (results.size() <= 1) return results.toArray(new ResolveResult[0]);
|
||||||
|
FileEditorManager editorManager = FileEditorManager.getInstance(project);
|
||||||
|
List<ResolveResult> openFiles = new ArrayList<>();
|
||||||
|
for (ResolveResult result : results) {
|
||||||
|
PsiElement element = result.getElement();
|
||||||
|
if (element != null) {
|
||||||
|
PsiFile file = element.getContainingFile();
|
||||||
|
if (file != null && file.getVirtualFile() != null && editorManager.isFileOpen(file.getVirtualFile())) {
|
||||||
|
openFiles.add(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!openFiles.isEmpty()) {
|
||||||
|
return openFiles.toArray(new ResolveResult[0]);
|
||||||
|
}
|
||||||
|
return results.toArray(new ResolveResult[0]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,12 @@
|
|||||||
]]></description>
|
]]></description>
|
||||||
|
|
||||||
<change-notes><![CDATA[
|
<change-notes><![CDATA[
|
||||||
|
<h2>[3.2.8]</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Cross-file Reference Resolution:</strong> Enhanced resolution of Datasets and Fields (e.g., DS-MASTER) to search upward across included `.frml` files.</li>
|
||||||
|
<li><strong>Opened File Priority:</strong> Reference navigation (Ctrl+Click) now prioritizes linking directly to files currently opened in editor tabs when multiple matches exist.</li>
|
||||||
|
<li><strong>Contextual AJAX-OPTION Completion:</strong> Code completion for `DATASET` under `<AJAX-OPTION>` is now strictly isolated to suggest entries defined exclusively in the module's `ajax.xml`.</li>
|
||||||
|
</ul>
|
||||||
<h2>[3.2.7]</h2>
|
<h2>[3.2.7]</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>Persistent Generation Context:</strong> Generator now remembers the last used directory for Action Beans and Dataset XMLs independently per project.</li>
|
<li><strong>Persistent Generation Context:</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