feat(config): persistent context, path portability, and refined browsing

- Generator now remembers last used directory for Action Beans and Datasets independently per project.
- Added settings to customize automatic file opening and table limits.
- Enforced project-relative paths for i18n/XSD for better project portability.
- Refined File Browser to start at current directory with strict project scope.
- Implemented DynFormAnnotator for structural validation.
- Bumped plugin version to 3.2.7.
This commit is contained in:
2026-04-22 16:04:18 +07:00
parent d42a514f44
commit 8f011e637b
5 changed files with 106 additions and 16 deletions

View File

@@ -1 +1 @@
2026-04-18 2026-04-22

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.6" version = "3.2.7"
repositories { repositories {
mavenCentral() mavenCentral()
@@ -38,18 +38,19 @@ intellijPlatform {
} }
changeNotes = """ changeNotes = """
<h2>[3.2.7]</h2>
<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>Configurable Auto-Open:</strong> Added new settings to toggle automatic file opening after generation and customize the table limit.</li>
<li><strong>Enhanced Path Portability:</strong> i18n and XSD configurations now strictly use project-relative paths, ensuring settings remain valid across different environments.</li>
<li><strong>Refined File Navigation:</strong> Improved the File Browser to prioritize starting at the current configured directory while maintaining strict project-scope visibility.</li>
</ul>
<h2>[3.2.6]</h2> <h2>[3.2.6]</h2>
<ul> <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>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>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> <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> </ul>
<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.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

@@ -2,6 +2,8 @@ package com.sdk.dynform.tools.dynform;
import com.intellij.codeInsight.completion.*; import com.intellij.codeInsight.completion.*;
import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.patterns.PlatformPatterns; import com.intellij.patterns.PlatformPatterns;
import com.intellij.patterns.XmlPatterns; import com.intellij.patterns.XmlPatterns;
import com.intellij.psi.*; import com.intellij.psi.*;
@@ -229,6 +231,20 @@ public class DynFormCompletionContributor extends CompletionContributor {
addFormFieldsForNameRecursive(parameters.getOriginalFile(), resultSet, new HashSet<>()); addFormFieldsForNameRecursive(parameters.getOriginalFile(), resultSet, new HashSet<>());
} }
}); });
// XML completion for INCLUDE:FILE
extend(CompletionType.BASIC, XmlPatterns.psiElement()
.inside(XmlPatterns.xmlAttributeValue()
.withParent(XmlPatterns.xmlAttribute().withName("FILE")
.withParent(XmlPatterns.xmlTag().withName("INCLUDE")))),
new CompletionProvider<CompletionParameters>() {
@Override
protected void addCompletions(@NotNull CompletionParameters parameters,
@NotNull ProcessingContext context,
@NotNull CompletionResultSet resultSet) {
addFileIncludeCompletions(parameters, resultSet);
}
});
} }
private void addFieldsInTagRecursive(XmlTag container, @NotNull CompletionResultSet resultSet) { private void addFieldsInTagRecursive(XmlTag container, @NotNull CompletionResultSet resultSet) {
@@ -254,7 +270,7 @@ 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) {
if (file == null || !visited.add(file)) return; if (file == null || !visited.add(file)) return;
// 1. Add datasets from current file // 1. Add datasets from current file
addDatasetsInFile(file, resultSet); addDatasetsInFile(file, resultSet);
@@ -391,7 +407,7 @@ public class DynFormCompletionContributor extends CompletionContributor {
private PsiElement findDatasetInFileRecursiveForCompletion(PsiFile file, String id, Set<PsiFile> visited) { private PsiElement findDatasetInFileRecursiveForCompletion(PsiFile file, String id, Set<PsiFile> visited) {
if (file == null || !visited.add(file)) return null; 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;
@@ -455,6 +471,78 @@ public class DynFormCompletionContributor extends CompletionContributor {
return null; return null;
} }
private void addFileIncludeCompletions(@NotNull CompletionParameters parameters, @NotNull CompletionResultSet resultSet) {
String value = parameters.getPosition().getText().replace(CompletionUtil.DUMMY_IDENTIFIER_TRIMMED, "");
PsiFile contextFile = parameters.getOriginalFile();
Project project = contextFile.getProject();
if (value.startsWith("#")) {
// Suggest files in the current module's view/frm
VirtualFile moduleDir = DynFormPathUtils.findModuleDir(contextFile);
if (moduleDir != null) {
VirtualFile frmDir = moduleDir.findFileByRelativePath("view/frm");
if (frmDir != null) {
addFrmlFilesRecursive(frmDir, "#", "", resultSet);
}
}
} else if (value.startsWith("/")) {
// Suggest modules or files in modules
String path = value.substring(1);
int firstSlash = path.indexOf("/");
if (firstSlash < 0) {
// Suggest modules
List<String> modules = DynFormPathUtils.getAllModules(project);
for (String module : modules) {
resultSet.addElement(LookupElementBuilder.create("/" + module + "/")
.withIcon(com.intellij.icons.AllIcons.Nodes.Module)
.withPresentableText("/" + module));
}
} else {
// Suggest files in the selected module
String moduleName = path.substring(0, firstSlash);
VirtualFile moduleBase = DynFormPathUtils.getModuleBaseDir(project);
if (moduleBase != null) {
VirtualFile frmDir = moduleBase.findFileByRelativePath(moduleName + "/view/frm");
if (frmDir != null) {
addFrmlFilesRecursive(frmDir, "/" + moduleName + "/", "", resultSet);
}
}
}
} else {
// Suggest relative files (basic implementation: current directory)
VirtualFile currentDir = contextFile.getVirtualFile().getParent();
if (currentDir != null) {
for (VirtualFile child : currentDir.getChildren()) {
if (child.isDirectory()) {
resultSet.addElement(LookupElementBuilder.create(child.getName() + "/")
.withIcon(com.intellij.icons.AllIcons.Nodes.Folder));
} else if (child.getName().endsWith(".frml")) {
resultSet.addElement(LookupElementBuilder.create(child.getName())
.withIcon(com.intellij.icons.AllIcons.FileTypes.Xml));
}
}
}
// Also suggest starting with # or /
resultSet.addElement(LookupElementBuilder.create("#")
.withTailText(" (Current Module)"));
resultSet.addElement(LookupElementBuilder.create("/")
.withTailText(" (Cross Module)"));
}
}
private void addFrmlFilesRecursive(VirtualFile dir, String prefix, String subPath, @NotNull CompletionResultSet resultSet) {
for (VirtualFile child : dir.getChildren()) {
String currentSubPath = subPath.isEmpty() ? child.getName() : subPath + "/" + child.getName();
if (child.isDirectory()) {
addFrmlFilesRecursive(child, prefix, currentSubPath, resultSet);
} else if (child.getName().endsWith(".frml")) {
resultSet.addElement(LookupElementBuilder.create(prefix + currentSubPath)
.withIcon(com.intellij.icons.AllIcons.FileTypes.Xml)
.withPresentableText(currentSubPath));
}
}
}
private void addFormFieldsForNameRecursive(PsiFile file, @NotNull CompletionResultSet resultSet, Set<PsiFile> visited) { private void addFormFieldsForNameRecursive(PsiFile file, @NotNull CompletionResultSet resultSet, Set<PsiFile> visited) {
if (file == null || !visited.add(file)) return; if (file == null || !visited.add(file)) return;
if (!(file instanceof XmlFile xmlFile)) return; if (!(file instanceof XmlFile xmlFile)) return;

View File

@@ -34,18 +34,19 @@
]]></description> ]]></description>
<change-notes><![CDATA[ <change-notes><![CDATA[
<h2>[3.2.7]</h2>
<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>Configurable Auto-Open:</strong> Added new settings to toggle automatic file opening after generation and customize the table limit.</li>
<li><strong>Enhanced Path Portability:</strong> i18n and XSD configurations now strictly use project-relative paths, ensuring settings remain valid across different environments.</li>
<li><strong>Refined File Navigation:</strong> Improved the File Browser to prioritize starting at the current configured directory while maintaining strict project-scope visibility.</li>
</ul>
<h2>[3.2.6]</h2> <h2>[3.2.6]</h2>
<ul> <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>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>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> <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> </ul>
<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> <h2>[3.2.4]</h2>
<ul> <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>Persistent Generation Directories:</strong> Generator now remembers the last used directory for Action Beans and Dataset XMLs independently per project.</li>