From e3a1bded772edcf742d2d31bba5fde51aae18442 Mon Sep 17 00:00:00 2001 From: Dan Timofte Date: Sat, 5 Oct 2024 14:34:38 +0300 Subject: [PATCH] Add inspection to replace component reference with full definition (#20) * resolve and show documentation for field and group * remove unused import * Add Inspection to replace component reference with full definition * remove unused imports --------- Co-authored-by: Dan Timofte --- CHANGELOG.md | 5 +- README.md | 22 ++-- .../quant/quickfixspec/common/PsiUtils.java | 6 +- .../ReplaceWithDefinitionInspection.java | 103 ++++++++++++++++++ src/main/resources/META-INF/plugin.xml | 1 + .../ReplaceWithDefinition.html | 35 ++++++ 6 files changed, 155 insertions(+), 17 deletions(-) create mode 100644 src/main/java/ac/quant/quickfixspec/inspections/ReplaceWithDefinitionInspection.java create mode 100644 src/main/resources/inspectionDescriptions/ReplaceWithDefinition.html diff --git a/CHANGELOG.md b/CHANGELOG.md index 3957b2c..8f2769f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,4 +8,7 @@ - v0.0.1 Add tag number next to the field name - v0.1.0 Add type next to the field name - v0.2.0 Add possibility to ctrl/cmd + click on the component name to navigate to the definition -- v0.2.0 when mouse hovering over a component name, display the component definition +- v0.2.0 Mouse hovering over a component name will display the component definition +- v0.3.0 Cmd click for fields and groups to go to the field definition +- v0.3.0 Mouse hovering over a field or group name will display the field definition +- v0.3.0 Add Inspection to replace component reference with the full component definition diff --git a/README.md b/README.md index 5ea0fae..9309659 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,20 @@ # quickfix-spec ![Build](https://github.com/dantimofte/quickfix-spec/workflows/Build/badge.svg) -[![Version](https://img.shields.io/jetbrains/plugin/v/MARKETPLACE_ID.svg)](https://plugins.jetbrains.com/plugin/MARKETPLACE_ID) -[![Downloads](https://img.shields.io/jetbrains/plugin/d/MARKETPLACE_ID.svg)](https://plugins.jetbrains.com/plugin/MARKETPLACE_ID) +[![Version](https://img.shields.io/jetbrains/plugin/v/MARKETPLACE_ID.svg)](https://plugins.jetbrains.com/plugin/25332-quickfix-xml-spec) +[![Downloads](https://img.shields.io/jetbrains/plugin/d/MARKETPLACE_ID.svg)](https://plugins.jetbrains.com/plugin/25332-quickfix-xml-spec) ## Template ToDo list - [x] Create a new [IntelliJ Platform Plugin Template][template] project. - [x] Get familiar with the [template documentation][template]. -- [ ] Adjust the [pluginGroup](./gradle.properties) and [pluginName](./gradle.properties), as well as the [id](./src/main/resources/META-INF/plugin.xml) and [sources package](./src/main/kotlin). -- [ ] Adjust the plugin description in `README` (see [Tips][docs:plugin-description]) -- [ ] Review the [Legal Agreements](https://plugins.jetbrains.com/docs/marketplace/legal-agreements.html?from=IJPluginTemplate). -- [ ] [Publish a plugin manually](https://plugins.jetbrains.com/docs/intellij/publishing-plugin.html?from=IJPluginTemplate) for the first time. -- [ ] Set the `MARKETPLACE_ID` in the above README badges. You can obtain it once the plugin is published to JetBrains Marketplace. -- [ ] Set the [Plugin Signing](https://plugins.jetbrains.com/docs/intellij/plugin-signing.html?from=IJPluginTemplate) related [secrets](https://github.com/JetBrains/intellij-platform-plugin-template#environment-variables). -- [ ] Set the [Deployment Token](https://plugins.jetbrains.com/docs/marketplace/plugin-upload.html?from=IJPluginTemplate). -- [ ] Click the Watch button on the top of the [IntelliJ Platform Plugin Template][template] to be notified about releases containing new features and fixes. +- [x] Adjust the [pluginGroup](./gradle.properties) and [pluginName](./gradle.properties), as well as the [id](./src/main/resources/META-INF/plugin.xml) and [sources package](./src/main/kotlin). +- [x] Adjust the plugin description in `README` (see [Tips][docs:plugin-description]) +- [x] Review the [Legal Agreements](https://plugins.jetbrains.com/docs/marketplace/legal-agreements.html?from=IJPluginTemplate). +- [X] [Publish a plugin manually](https://plugins.jetbrains.com/docs/intellij/publishing-plugin.html?from=IJPluginTemplate) for the first time. +- [x] Set the `MARKETPLACE_ID` in the above README badges. You can obtain it once the plugin is published to JetBrains Marketplace. +- [x] Set the [Plugin Signing](https://plugins.jetbrains.com/docs/intellij/plugin-signing.html?from=IJPluginTemplate) related [secrets](https://github.com/JetBrains/intellij-platform-plugin-template#environment-variables). +- [x] Set the [Deployment Token](https://plugins.jetbrains.com/docs/marketplace/plugin-upload.html?from=IJPluginTemplate). +- [x] Click the Watch button on the top of the [IntelliJ Platform Plugin Template][template] to be notified about releases containing new features and fixes. This Plugin is going to make it easier to work with the QuickFix XML Specification. @@ -35,7 +35,7 @@ This Plugin is going to make it easier to work with the QuickFix XML Specificati - Using JetBrains Marketplace: - Go to [JetBrains Marketplace](https://plugins.jetbrains.com/plugin/MARKETPLACE_ID) and install it by clicking the Install to ... button in case your IDE is running. + Go to [JetBrains Marketplace](https://plugins.jetbrains.com/plugin/25332-quickfix-xml-spec) and install it by clicking the Install to ... button in case your IDE is running. You can also download the [latest release](https://plugins.jetbrains.com/plugin/MARKETPLACE_ID/versions) from JetBrains Marketplace and install it manually using Settings/Preferences > Plugins > ⚙️ > Install plugin from disk... diff --git a/src/main/java/ac/quant/quickfixspec/common/PsiUtils.java b/src/main/java/ac/quant/quickfixspec/common/PsiUtils.java index a50c8a4..776690f 100644 --- a/src/main/java/ac/quant/quickfixspec/common/PsiUtils.java +++ b/src/main/java/ac/quant/quickfixspec/common/PsiUtils.java @@ -68,10 +68,6 @@ public static boolean isTagDeclaration(XmlAttributeValue valueElement) { final String parentTagName = parentTag.getName(); - if (DEFINITION_GROUP_NAME.containsValue(parentTagName)) { - return true; - } - - return false; + return DEFINITION_GROUP_NAME.containsValue(parentTagName); } } diff --git a/src/main/java/ac/quant/quickfixspec/inspections/ReplaceWithDefinitionInspection.java b/src/main/java/ac/quant/quickfixspec/inspections/ReplaceWithDefinitionInspection.java new file mode 100644 index 0000000..6e3809a --- /dev/null +++ b/src/main/java/ac/quant/quickfixspec/inspections/ReplaceWithDefinitionInspection.java @@ -0,0 +1,103 @@ +package ac.quant.quickfixspec.inspections; + +import com.intellij.codeInspection.LocalInspectionTool; +import com.intellij.codeInspection.ProblemsHolder; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.SmartPointerManager; +import com.intellij.psi.SmartPsiElementPointer; +import com.intellij.psi.xml.XmlTag; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; + +import static ac.quant.quickfixspec.common.PsiUtils.*; + +public class ReplaceWithDefinitionInspection extends LocalInspectionTool { + + @Override + public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) { + return new PsiElementVisitor() { + @Override + public void visitElement(@NotNull PsiElement element) { + if (isNotComponentTagReference(element)) { + return; + } + + final XmlTag rootTag = getRootTag(element); + final XmlTag tag = (XmlTag) element; + if (rootTag == null) { + return; + } + + final String componentName = tag.getAttributeValue("name"); + + + XmlTag definition = findDefinition(componentName, tag.getName(), rootTag); + if (definition != null) { + holder.registerProblem(tag, "Replace with definition", new ReplaceWithDefinitionQuickFix(definition)); + } + } + }; + } + + private boolean isNotComponentTagReference(@NotNull PsiElement element) { + if (!(element instanceof XmlTag tag)) { + return true; + } + + final XmlTag parentTag = tag.getParentTag(); + if (parentTag == null) { + return true; + } + + if (parentTag.getName().equals("components")) { + return true; + } + + // if the tag is not self-closing that means it is a definition + if (tag.getSubTags().length > 0) { + return true; + } + + return !tag.getName().equals("component"); + } + + private static class ReplaceWithDefinitionQuickFix implements LocalQuickFix { + @SafeFieldForPreview + private final SmartPsiElementPointer definitionPointer; + + ReplaceWithDefinitionQuickFix(XmlTag definition) { + this.definitionPointer = SmartPointerManager.createPointer(definition); + } + + @Override + public @NotNull String getName() { + return "Replace with definition"; + } + + @Override + public @NotNull String getFamilyName() { + return getName(); + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) throws IncorrectOperationException { + // Get the tag that is to be replaced + XmlTag tagToReplace = (XmlTag) descriptor.getPsiElement(); + + // Retrieve the full definition of the from the pointer + XmlTag definition = definitionPointer.getElement(); + + if (definition != null) { + // Create a copy of the definition tag to be inserted + XmlTag newTag = (XmlTag) definition.copy(); + + // Replace the original tag with the full definition + tagToReplace.replace(newTag); + } + } + } +} \ No newline at end of file diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 88c9549..ff1ff15 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -35,6 +35,7 @@ + diff --git a/src/main/resources/inspectionDescriptions/ReplaceWithDefinition.html b/src/main/resources/inspectionDescriptions/ReplaceWithDefinition.html new file mode 100644 index 0000000..cfa50bb --- /dev/null +++ b/src/main/resources/inspectionDescriptions/ReplaceWithDefinition.html @@ -0,0 +1,35 @@ + + +

+ Detects shorthand <component> tags (e.g., <component name="compName1" />) within + <message> sections and suggests replacing them with their full definition from the + <components> section of the same XML file. +

+ +

Example

+

+<message>
+  <component name="compName1" />
+</message>
+
+<components>
+  <component name="compName1">
+    <field name="aa" />
+    <field name="bb" />
+  </component>
+</components>
+
+ +

+ Suggestion: Expand <component name="compName1" /> to: +

+ +

+<component name="compName1">
+  <field name="aa" />
+  <field name="bb" />
+</component>
+
+ + +