From 9dfa41a3411253c8cc227935873c43bc80babc69 Mon Sep 17 00:00:00 2001
From: Vitaliy Boyko <v.boyko@atwix.com>
Date: Tue, 24 Mar 2020 19:48:12 +0200
Subject: [PATCH 1/3] MVP implementation of the New Magento 2 Module action

---
 resources/META-INF/plugin.xml                 |   6 +
 .../internal/Magento Module Composer.html     |   4 +
 .../internal/Magento Module Composer.json.ft  |  17 ++
 .../Magento Module Registration Php.html      |   4 +
 .../Magento Module Registration Php.php.ft    |   6 +
 .../internal/Magento Module XML.html          |   4 +
 .../internal/Magento Module XML.xml.ft        |  10 +
 .../{magento2-module.png => icons/module.png} | Bin
 .../icons/webapi.png                          | Bin
 .../idea/magento2plugin/MagentoIcons.java     |   1 +
 .../actions/generation/NewModuleAction.java   |  61 ++++++
 .../dialog/NewMagentoModuleDialog.form        | 145 ++++++++++++++
 .../dialog/NewMagentoModuleDialog.java        | 185 ++++++++++++++++++
 .../NewMagentoModuleDialogValidator.java      |  66 +++++++
 .../generator/DirectoryGenerator.java         |  33 ++++
 .../generator/FileFromTemplateGenerator.java  | 122 ++++++++++++
 .../generator/data/ModuleDirectoriesData.java |  30 +++
 .../magento/files/ComposerJson.java           |  36 ++++
 .../magento/files/ModuleFileInterface.java    |  13 ++
 .../magento/files/ModuleXml.java              |  36 ++++
 .../magento/files/RegistrationPhp.java        |  36 ++++
 .../magento/packages/MagentoPackages.java     |   9 +
 .../util/CamelCaseToHyphen.java               |  31 +++
 .../idea/magento2plugin/util/Regex.java       |  11 ++
 24 files changed, 866 insertions(+)
 create mode 100644 resources/fileTemplates/internal/Magento Module Composer.html
 create mode 100644 resources/fileTemplates/internal/Magento Module Composer.json.ft
 create mode 100644 resources/fileTemplates/internal/Magento Module Registration Php.html
 create mode 100644 resources/fileTemplates/internal/Magento Module Registration Php.php.ft
 create mode 100644 resources/fileTemplates/internal/Magento Module XML.html
 create mode 100644 resources/fileTemplates/internal/Magento Module XML.xml.ft
 rename resources/{magento2-module.png => icons/module.png} (100%)
 rename {src/com/magento/idea/magento2plugin => resources}/icons/webapi.png (100%)
 create mode 100644 src/com/magento/idea/magento2plugin/actions/generation/NewModuleAction.java
 create mode 100644 src/com/magento/idea/magento2plugin/actions/generation/dialog/NewMagentoModuleDialog.form
 create mode 100644 src/com/magento/idea/magento2plugin/actions/generation/dialog/NewMagentoModuleDialog.java
 create mode 100644 src/com/magento/idea/magento2plugin/actions/generation/dialog/validator/NewMagentoModuleDialogValidator.java
 create mode 100644 src/com/magento/idea/magento2plugin/actions/generation/generator/DirectoryGenerator.java
 create mode 100644 src/com/magento/idea/magento2plugin/actions/generation/generator/FileFromTemplateGenerator.java
 create mode 100644 src/com/magento/idea/magento2plugin/actions/generation/generator/data/ModuleDirectoriesData.java
 create mode 100644 src/com/magento/idea/magento2plugin/magento/files/ComposerJson.java
 create mode 100644 src/com/magento/idea/magento2plugin/magento/files/ModuleFileInterface.java
 create mode 100644 src/com/magento/idea/magento2plugin/magento/files/ModuleXml.java
 create mode 100644 src/com/magento/idea/magento2plugin/magento/files/RegistrationPhp.java
 create mode 100644 src/com/magento/idea/magento2plugin/magento/packages/MagentoPackages.java
 create mode 100644 src/com/magento/idea/magento2plugin/util/CamelCaseToHyphen.java
 create mode 100644 src/com/magento/idea/magento2plugin/util/Regex.java

diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml
index d888fd90f..77c70843e 100644
--- a/resources/META-INF/plugin.xml
+++ b/resources/META-INF/plugin.xml
@@ -53,6 +53,10 @@
                     description="Create Magento around plugin method."/>
             <add-to-group group-id="PhpGenerateGroup" anchor="last"/>
         </group>
+        <group id="MagentoNewGroup">
+            <action id="Magento2NewModule" class="com.magento.idea.magento2plugin.actions.generation.NewModuleAction"/>
+            <add-to-group group-id="NewGroup" anchor="after" relative-to-action="NewDir"/>
+        </group>
 
     </actions>
 
@@ -104,6 +108,7 @@
                          groupName="Magento"                                 enabledByDefault="true" level="ERROR"
                          implementationClass="com.magento.idea.magento2plugin.inspections.php.PluginInspection"/>
         <libraryRoot id=".phpstorm.meta.php" path=".phpstorm.meta.php/" runtime="false"/>
+
     </extensions>
 
     <application-components>
@@ -118,4 +123,5 @@
         <!-- Add your actions here -->
     </actions>
 
+
 </idea-plugin>
diff --git a/resources/fileTemplates/internal/Magento Module Composer.html b/resources/fileTemplates/internal/Magento Module Composer.html
new file mode 100644
index 000000000..6bfc7988e
--- /dev/null
+++ b/resources/fileTemplates/internal/Magento Module Composer.html	
@@ -0,0 +1,4 @@
+<html>
+<body>
+</body>
+</html>
\ No newline at end of file
diff --git a/resources/fileTemplates/internal/Magento Module Composer.json.ft b/resources/fileTemplates/internal/Magento Module Composer.json.ft
new file mode 100644
index 000000000..5bf88ab4a
--- /dev/null
+++ b/resources/fileTemplates/internal/Magento Module Composer.json.ft	
@@ -0,0 +1,17 @@
+{
+  "name": "${COMPOSER_PACKAGE_NAME}",
+  "version": "${MODULE_VERSION}",
+  "description": "${MODULE_DESCRIPTION}",
+  "type": "magento2-module",
+   #if (${DEPENDENCIES}) "require": {
+   ${DEPENDENCIES} }, #end
+#if (${LICENSE})   ${LICENSE}, #end
+  "autoload": {
+    "files": [
+      "registration.php"
+    ],
+    "psr-4": {
+      "${PACKAGE}\\\\${MODULE_NAME}\\": ""
+    }
+  }
+}
\ No newline at end of file
diff --git a/resources/fileTemplates/internal/Magento Module Registration Php.html b/resources/fileTemplates/internal/Magento Module Registration Php.html
new file mode 100644
index 000000000..6bfc7988e
--- /dev/null
+++ b/resources/fileTemplates/internal/Magento Module Registration Php.html	
@@ -0,0 +1,4 @@
+<html>
+<body>
+</body>
+</html>
\ No newline at end of file
diff --git a/resources/fileTemplates/internal/Magento Module Registration Php.php.ft b/resources/fileTemplates/internal/Magento Module Registration Php.php.ft
new file mode 100644
index 000000000..77b88d501
--- /dev/null
+++ b/resources/fileTemplates/internal/Magento Module Registration Php.php.ft	
@@ -0,0 +1,6 @@
+<?php
+#parse("PHP File Header.php")
+
+use Magento\Framework\Component\ComponentRegistrar;
+
+ComponentRegistrar::register(ComponentRegistrar::MODULE, '${PACKAGE}_${MODULE_NAME}', __DIR__);
diff --git a/resources/fileTemplates/internal/Magento Module XML.html b/resources/fileTemplates/internal/Magento Module XML.html
new file mode 100644
index 000000000..6bfc7988e
--- /dev/null
+++ b/resources/fileTemplates/internal/Magento Module XML.html	
@@ -0,0 +1,4 @@
+<html>
+<body>
+</body>
+</html>
\ No newline at end of file
diff --git a/resources/fileTemplates/internal/Magento Module XML.xml.ft b/resources/fileTemplates/internal/Magento Module XML.xml.ft
new file mode 100644
index 000000000..895b81744
--- /dev/null
+++ b/resources/fileTemplates/internal/Magento Module XML.xml.ft	
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
+    #if (${SEQUENCES})
+        <module name="${PACKAGE}_${MODULE_NAME}">
+            ${SEQUENCES}
+        </module>
+    #else
+        <module name="${PACKAGE}_${MODULE_NAME}" />
+    #end
+</config>
diff --git a/resources/magento2-module.png b/resources/icons/module.png
similarity index 100%
rename from resources/magento2-module.png
rename to resources/icons/module.png
diff --git a/src/com/magento/idea/magento2plugin/icons/webapi.png b/resources/icons/webapi.png
similarity index 100%
rename from src/com/magento/idea/magento2plugin/icons/webapi.png
rename to resources/icons/webapi.png
diff --git a/src/com/magento/idea/magento2plugin/MagentoIcons.java b/src/com/magento/idea/magento2plugin/MagentoIcons.java
index ae139608a..82de250f1 100644
--- a/src/com/magento/idea/magento2plugin/MagentoIcons.java
+++ b/src/com/magento/idea/magento2plugin/MagentoIcons.java
@@ -13,4 +13,5 @@
  */
 public class MagentoIcons {
     public static final Icon WEB_API = IconLoader.getIcon("icons/webapi.png");
+    public static final Icon MODULE = IconLoader.getIcon("/icons/module.png");
 }
diff --git a/src/com/magento/idea/magento2plugin/actions/generation/NewModuleAction.java b/src/com/magento/idea/magento2plugin/actions/generation/NewModuleAction.java
new file mode 100644
index 000000000..e8f1b7887
--- /dev/null
+++ b/src/com/magento/idea/magento2plugin/actions/generation/NewModuleAction.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+package com.magento.idea.magento2plugin.actions.generation;
+
+import com.intellij.ide.IdeView;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.actionSystem.LangDataKeys;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiDirectory;
+import com.intellij.psi.PsiFile;
+import com.magento.idea.magento2plugin.MagentoIcons;
+import com.magento.idea.magento2plugin.actions.generation.dialog.NewMagentoModuleDialog;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class NewModuleAction extends com.intellij.openapi.actionSystem.AnAction {
+    public static String ACTION_NAME = "New Magento 2 Module";
+    public static String ACTION_DESCRIPTION = "Create a new Magento 2 module";
+
+    NewModuleAction() {
+        super(ACTION_NAME, ACTION_DESCRIPTION, MagentoIcons.MODULE);
+    }
+    @Override
+    public void actionPerformed(@NotNull AnActionEvent e) {
+        DataContext dataContext = e.getDataContext();
+        IdeView view = LangDataKeys.IDE_VIEW.getData(dataContext);
+        if (view != null) {
+            Project project = CommonDataKeys.PROJECT.getData(dataContext);
+            if (project != null) {
+                PsiDirectory initialBaseDir = view.getOrChooseDirectory();
+                if (initialBaseDir != null) {
+                    this.invoke(project, initialBaseDir, this.getFile(dataContext), view, CommonDataKeys.EDITOR.getData(dataContext));
+                }
+            }
+        }
+    }
+
+    public void invoke(@NotNull Project project, @NotNull PsiDirectory initialBaseDir, @Nullable PsiFile file, @Nullable IdeView view, @Nullable Editor editor) {
+        NewMagentoModuleDialog.open(project, initialBaseDir, file, view, editor);
+    }
+
+    @Override
+    public boolean isDumbAware() {
+        return false;
+    }
+
+    @NotNull
+    private String getActionName() {
+        return this.getTemplatePresentation().getText();
+    }
+
+    public PsiFile getFile(DataContext dataContext) {
+        return CommonDataKeys.PSI_FILE.getData(dataContext);
+    }
+}
+
diff --git a/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewMagentoModuleDialog.form b/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewMagentoModuleDialog.form
new file mode 100644
index 000000000..cd3eca240
--- /dev/null
+++ b/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewMagentoModuleDialog.form
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.magento.idea.magento2plugin.actions.generation.dialog.NewMagentoModuleDialog">
+  <grid id="cbd77" binding="contentPane" layout-manager="GridLayoutManager" row-count="4" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+    <margin top="10" left="10" bottom="10" right="10"/>
+    <constraints>
+      <xy x="48" y="54" width="462" height="297"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <grid id="94766" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+        <margin top="0" left="0" bottom="0" right="0"/>
+        <constraints>
+          <grid row="3" column="0" row-span="1" col-span="2" vsize-policy="1" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <hspacer id="98af6">
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+            </constraints>
+          </hspacer>
+          <grid id="9538f" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="true" same-size-vertically="false" hgap="-1" vgap="-1">
+            <margin top="0" left="0" bottom="0" right="0"/>
+            <constraints>
+              <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties/>
+            <border type="none"/>
+            <children>
+              <component id="e7465" class="javax.swing.JButton" binding="buttonOK">
+                <constraints>
+                  <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+                </constraints>
+                <properties>
+                  <text value="OK"/>
+                </properties>
+              </component>
+              <component id="5723f" class="javax.swing.JButton" binding="buttonCancel">
+                <constraints>
+                  <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
+                </constraints>
+                <properties>
+                  <text value="Cancel"/>
+                </properties>
+              </component>
+            </children>
+          </grid>
+        </children>
+      </grid>
+      <grid id="e3588" layout-manager="GridLayoutManager" row-count="3" column-count="4" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+        <margin top="0" left="0" bottom="0" right="0"/>
+        <constraints>
+          <grid row="0" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+        <border type="none"/>
+        <children>
+          <component id="ad3ba" class="javax.swing.JTextField" binding="packageName" default-binding="true">
+            <constraints>
+              <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+                <preferred-size width="150" height="-1"/>
+              </grid>
+            </constraints>
+            <properties/>
+            <clientProperties>
+              <html.disable class="java.lang.Boolean" value="true"/>
+            </clientProperties>
+          </component>
+          <component id="3fe2b" class="javax.swing.JLabel" binding="packageNameLabel">
+            <constraints>
+              <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <text value="Package Name"/>
+            </properties>
+          </component>
+          <component id="e4858" class="javax.swing.JTextField" binding="moduleName">
+            <constraints>
+              <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+                <preferred-size width="150" height="-1"/>
+              </grid>
+            </constraints>
+            <properties/>
+            <clientProperties>
+              <html.disable class="java.lang.Boolean" value="true"/>
+            </clientProperties>
+          </component>
+          <component id="22b5e" class="javax.swing.JLabel" binding="moduleNameLabel">
+            <constraints>
+              <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <text value="Module Name"/>
+            </properties>
+          </component>
+          <component id="423dc" class="javax.swing.JTextField" binding="moduleVersion">
+            <constraints>
+              <grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
+                <preferred-size width="150" height="-1"/>
+              </grid>
+            </constraints>
+            <properties>
+              <text value="1.0.0"/>
+            </properties>
+            <clientProperties>
+              <html.disable class="java.lang.Boolean" value="true"/>
+            </clientProperties>
+          </component>
+          <component id="2af8d" class="javax.swing.JLabel" binding="moduleVersionLabel">
+            <constraints>
+              <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+            </constraints>
+            <properties>
+              <text value="Module Version"/>
+            </properties>
+          </component>
+        </children>
+      </grid>
+      <component id="4c5ac" class="javax.swing.JTextArea" binding="moduleDescription">
+        <constraints>
+          <grid row="2" column="0" row-span="1" col-span="2" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
+            <preferred-size width="150" height="50"/>
+          </grid>
+        </constraints>
+        <properties>
+          <text value="N/A"/>
+          <toolTipText value="Module Description"/>
+        </properties>
+        <clientProperties>
+          <html.disable class="java.lang.Boolean" value="true"/>
+        </clientProperties>
+      </component>
+      <component id="ec98d" class="javax.swing.JLabel" binding="moduleDescriptionLabel">
+        <constraints>
+          <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <text value="Module Description"/>
+        </properties>
+      </component>
+    </children>
+  </grid>
+</form>
diff --git a/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewMagentoModuleDialog.java b/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewMagentoModuleDialog.java
new file mode 100644
index 000000000..fa3cdd74d
--- /dev/null
+++ b/src/com/magento/idea/magento2plugin/actions/generation/dialog/NewMagentoModuleDialog.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+package com.magento.idea.magento2plugin.actions.generation.dialog;
+
+import com.intellij.ide.IdeView;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiDirectory;
+import com.intellij.psi.PsiFile;
+import com.magento.idea.magento2plugin.actions.generation.NewModuleAction;
+import com.magento.idea.magento2plugin.actions.generation.dialog.validator.NewMagentoModuleDialogValidator;
+import com.magento.idea.magento2plugin.actions.generation.generator.DirectoryGenerator;
+import com.magento.idea.magento2plugin.actions.generation.generator.FileFromTemplateGenerator;
+import com.magento.idea.magento2plugin.actions.generation.generator.data.ModuleDirectoriesData;
+import com.magento.idea.magento2plugin.magento.files.ComposerJson;
+import com.magento.idea.magento2plugin.magento.files.ModuleXml;
+import com.magento.idea.magento2plugin.magento.files.RegistrationPhp;
+import com.magento.idea.magento2plugin.magento.packages.MagentoPackages;
+import com.magento.idea.magento2plugin.util.CamelCaseToHyphen;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.Properties;
+
+public class NewMagentoModuleDialog extends JDialog {
+    @NotNull
+    private final Project project;
+    @NotNull
+    private final PsiDirectory initialBaseDir;
+    @Nullable
+    private final PsiFile file;
+    @Nullable
+    private final IdeView view;
+    @Nullable
+    private final Editor editor;
+    private final DirectoryGenerator directoryGenerator;
+    private final FileFromTemplateGenerator fileFromTemplateGenerator;
+    private final NewMagentoModuleDialogValidator validator;
+    private final CamelCaseToHyphen camelCaseToHyphen;
+    private JPanel contentPane;
+    private JButton buttonOK;
+    private JButton buttonCancel;
+    private JTextField packageName;
+    private JLabel packageNameLabel;
+    private JTextField moduleName;
+    private JLabel moduleNameLabel;
+    private JTextArea moduleDescription;
+    private JLabel moduleDescriptionLabel;
+    private JTextField moduleVersion;
+    private JLabel moduleVersionLabel;
+    private String detectedPackageName;
+
+    public NewMagentoModuleDialog(@NotNull Project project, @NotNull PsiDirectory initialBaseDir, @Nullable PsiFile file, @Nullable IdeView view, @Nullable Editor editor) {
+        this.project = project;
+        this.initialBaseDir = initialBaseDir;
+        this.file = file;
+        this.view = view;
+        this.editor = editor;
+        this.directoryGenerator = DirectoryGenerator.getInstance();
+        this.fileFromTemplateGenerator = FileFromTemplateGenerator.getInstance(project);
+        this.camelCaseToHyphen = CamelCaseToHyphen.getInstance();
+        this.validator = NewMagentoModuleDialogValidator.getInstance(this);
+        detectPackageName(initialBaseDir);
+        setContentPane(contentPane);
+        setModal(true);
+        getRootPane().setDefaultButton(buttonOK);
+        pushToMiddle();
+
+        buttonOK.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                onOK();
+            }
+        });
+
+        buttonCancel.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                onCancel();
+            }
+        });
+
+        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
+        addWindowListener(new WindowAdapter() {
+            public void windowClosing(WindowEvent e) {
+                onCancel();
+            }
+        });
+
+        contentPane.registerKeyboardAction(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                onCancel();
+            }
+        }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
+    }
+
+    private void detectPackageName(@NotNull PsiDirectory initialBaseDir) {
+        PsiDirectory parentDir = initialBaseDir.getParent();
+        if (parentDir != null && parentDir.toString().endsWith(MagentoPackages.PACKAGES_ROOT)) {
+            packageName.setVisible(false);
+            packageNameLabel.setVisible(false);
+            this.detectedPackageName = initialBaseDir.getName();
+        }
+    }
+
+    private void pushToMiddle() {
+        Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
+        this.setLocation(dim.width / 2  -this.getSize().width / 2, dim.height / 2 - this.getSize().height / 2);
+    }
+
+    private void onOK() {
+        if (!validator.validate()) {
+            return;
+        }
+        generateFiles();
+        this.setVisible(false);
+    }
+
+    private void generateFiles() {
+        PsiDirectory baseDir = detectedPackageName != null ? this.initialBaseDir.getParent() : this.initialBaseDir;
+        ModuleDirectoriesData moduleDirectoriesData = directoryGenerator.createModuleDirectories(getPackageName(), getModuleName(), baseDir);
+        Properties attributes = getAttributes();
+        PsiFile composerJson = fileFromTemplateGenerator.generate(ComposerJson.getInstance(), attributes, moduleDirectoriesData.getModuleDirectory(), NewModuleAction.ACTION_NAME);
+        if (composerJson == null) {
+            return;
+        }
+        PsiFile registrationPhp = fileFromTemplateGenerator.generate(RegistrationPhp.getInstance(), attributes, moduleDirectoriesData.getModuleDirectory(), NewModuleAction.ACTION_NAME);
+        if (registrationPhp == null) {
+            return;
+        }
+        fileFromTemplateGenerator.generate(ModuleXml.getInstance(), attributes, moduleDirectoriesData.getModuleEtcDirectory(), NewModuleAction.ACTION_NAME);
+    }
+
+    public String getPackageName() {
+        if (detectedPackageName != null) {
+            return detectedPackageName;
+        }
+        return this.packageName.getText().trim();
+    }
+
+    public String getModuleName() {
+        return this.moduleName.getText().trim();
+    }
+
+    public String getModuleDescription() {
+        return this.moduleDescription.getText().trim();
+    }
+
+    public String getModuleVersion() {
+        return this.moduleVersion.getText().trim();
+    }
+
+    private void onCancel() {
+        this.setVisible(false);
+    }
+
+    public static void open(@NotNull Project project, @NotNull PsiDirectory initialBaseDir, @Nullable PsiFile file, @Nullable IdeView view, @Nullable Editor editor) {
+        NewMagentoModuleDialog dialog = new NewMagentoModuleDialog(project, initialBaseDir, file, view, editor);
+        dialog.pack();
+        dialog.setVisible(true);
+    }
+
+    private Properties getAttributes() {
+        Properties attributes = new Properties();
+        this.fillAttributes(attributes);
+        return attributes;
+    }
+
+    private void fillAttributes(Properties attributes) {
+        attributes.setProperty("PACKAGE", getPackageName());
+        attributes.setProperty("MODULE_NAME", getModuleName());
+        attributes.setProperty("MODULE_DESCRIPTION", getModuleDescription());
+        attributes.setProperty("COMPOSER_PACKAGE_NAME", getComposerPackageName());
+        attributes.setProperty("MODULE_VERSION", getModuleVersion());
+    }
+
+    @NotNull
+    private String getComposerPackageName() {
+        return camelCaseToHyphen.convert(getPackageName())
+                .concat("/")
+                .concat(camelCaseToHyphen.convert(getModuleName()));
+    }
+}
diff --git a/src/com/magento/idea/magento2plugin/actions/generation/dialog/validator/NewMagentoModuleDialogValidator.java b/src/com/magento/idea/magento2plugin/actions/generation/dialog/validator/NewMagentoModuleDialogValidator.java
new file mode 100644
index 000000000..9c6c05fe3
--- /dev/null
+++ b/src/com/magento/idea/magento2plugin/actions/generation/dialog/validator/NewMagentoModuleDialogValidator.java
@@ -0,0 +1,66 @@
+package com.magento.idea.magento2plugin.actions.generation.dialog.validator;
+
+import com.magento.idea.magento2plugin.actions.generation.dialog.NewMagentoModuleDialog;
+import com.magento.idea.magento2plugin.util.Regex;
+import javax.swing.*;
+
+public class NewMagentoModuleDialogValidator {
+    private static NewMagentoModuleDialogValidator INSTANCE = null;
+    private NewMagentoModuleDialog dialog;
+
+    public static NewMagentoModuleDialogValidator getInstance(NewMagentoModuleDialog dialog) {
+        if (null == INSTANCE) {
+            INSTANCE = new NewMagentoModuleDialogValidator();
+        }
+        INSTANCE.dialog = dialog;
+        return INSTANCE;
+    }
+
+    public boolean validate()
+    {
+        String errorTitle = "Error";
+        String packageName = dialog.getPackageName();
+        if (packageName.length() == 0) {
+            JOptionPane.showMessageDialog(null, "Package Name must not be empty.", errorTitle, JOptionPane.ERROR_MESSAGE);
+            return false;
+        }
+
+        if (!packageName.matches(Regex.ALPHANUMERIC)) {
+            JOptionPane.showMessageDialog(null, "Package Name must contain letters and numbers only.", errorTitle, JOptionPane.ERROR_MESSAGE);
+            return false;
+        }
+
+        if (!Character.isUpperCase(packageName.charAt(0)) && !Character.isDigit(packageName.charAt(0))) {
+            JOptionPane.showMessageDialog(null, "Package Name must start from a number or a capital letter", errorTitle, JOptionPane.ERROR_MESSAGE);
+            return false;
+        }
+
+        String moduleName = dialog.getModuleName();
+        if (moduleName.length() == 0) {
+            JOptionPane.showMessageDialog(null, "Module Name must not be empty.", errorTitle, JOptionPane.ERROR_MESSAGE);
+            return false;
+        }
+
+        if (!moduleName.matches(Regex.ALPHANUMERIC)) {
+            JOptionPane.showMessageDialog(null, "Module Name must contain letters and numbers only.", errorTitle, JOptionPane.ERROR_MESSAGE);
+            return false;
+        }
+
+        if (!Character.isUpperCase(moduleName.charAt(0)) && !Character.isDigit(moduleName.charAt(0))) {
+            JOptionPane.showMessageDialog(null, "Module Name must start from a number or a capital letter", errorTitle, JOptionPane.ERROR_MESSAGE);
+            return false;
+        }
+
+        if (dialog.getModuleVersion().length() == 0) {
+            JOptionPane.showMessageDialog(null, "Module Version must not be empty.", errorTitle, JOptionPane.ERROR_MESSAGE);
+            return false;
+        }
+
+        if (dialog.getModuleDescription().length() == 0) {
+            JOptionPane.showMessageDialog(null, "Module Version must not be empty.", errorTitle, JOptionPane.ERROR_MESSAGE);
+            return false;
+        }
+
+        return true;
+    }
+}
diff --git a/src/com/magento/idea/magento2plugin/actions/generation/generator/DirectoryGenerator.java b/src/com/magento/idea/magento2plugin/actions/generation/generator/DirectoryGenerator.java
new file mode 100644
index 000000000..a2037adaa
--- /dev/null
+++ b/src/com/magento/idea/magento2plugin/actions/generation/generator/DirectoryGenerator.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+package com.magento.idea.magento2plugin.actions.generation.generator;
+
+import com.intellij.openapi.application.WriteAction;
+import com.intellij.psi.PsiDirectory;
+import com.magento.idea.magento2plugin.actions.generation.generator.data.ModuleDirectoriesData;
+import org.jetbrains.annotations.NotNull;
+
+public class DirectoryGenerator {
+    private static DirectoryGenerator INSTANCE = null;
+
+    public static DirectoryGenerator getInstance() {
+        if (null == INSTANCE) {
+            INSTANCE = new DirectoryGenerator();
+        }
+        return INSTANCE;
+    }
+
+    public ModuleDirectoriesData createModuleDirectories(@NotNull String packageName, @NotNull String moduleName, @NotNull PsiDirectory baseDirectory){
+        PsiDirectory packageDirectory = findOrCreateSubdirectory(baseDirectory, packageName);
+        PsiDirectory moduleDirectory = findOrCreateSubdirectory(packageDirectory, moduleName);
+        PsiDirectory moduleEtcDirectory = findOrCreateSubdirectory(moduleDirectory, "etc");
+        return new ModuleDirectoriesData(moduleDirectory, moduleEtcDirectory);
+    }
+
+    public PsiDirectory findOrCreateSubdirectory(@NotNull PsiDirectory parent, @NotNull String subdirName) {
+        final PsiDirectory sub = parent.findSubdirectory(subdirName);
+        return sub == null ? WriteAction.compute(() -> parent.createSubdirectory(subdirName)) : sub;
+    }
+}
diff --git a/src/com/magento/idea/magento2plugin/actions/generation/generator/FileFromTemplateGenerator.java b/src/com/magento/idea/magento2plugin/actions/generation/generator/FileFromTemplateGenerator.java
new file mode 100644
index 000000000..83d270524
--- /dev/null
+++ b/src/com/magento/idea/magento2plugin/actions/generation/generator/FileFromTemplateGenerator.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+package com.magento.idea.magento2plugin.actions.generation.generator;
+
+import com.intellij.ide.fileTemplates.DefaultTemplatePropertiesProvider;
+import com.intellij.ide.fileTemplates.FileTemplate;
+import com.intellij.ide.fileTemplates.FileTemplateManager;
+import com.intellij.lang.Language;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.command.CommandProcessor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.PsiDirectory;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiFileFactory;
+import com.intellij.psi.codeStyle.CodeStyleManager;
+import com.intellij.util.IncorrectOperationException;
+import com.magento.idea.magento2plugin.magento.files.ModuleFileInterface;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import java.io.File;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+
+public class FileFromTemplateGenerator {
+    private static FileFromTemplateGenerator INSTANCE = null;
+    private Project project;
+
+    public static FileFromTemplateGenerator getInstance(Project project) {
+        if (null == INSTANCE) {
+            INSTANCE = new FileFromTemplateGenerator();
+        }
+        INSTANCE.project = project;
+        return INSTANCE;
+    }
+
+    @Nullable
+    public PsiFile generate(@NotNull ModuleFileInterface moduleFile, @NotNull Properties attributes, @NotNull PsiDirectory baseDir, @NotNull String actionName) {
+        Ref<PsiFile> fileRef = new Ref(null);
+        Ref<String> exceptionRef = new Ref(null);
+        String filePath = baseDir.getText().concat("/").concat(moduleFile.getFileName());
+        CommandProcessor.getInstance().executeCommand(project, () -> {
+            Runnable run = () -> {
+                try {
+                    PsiFile file = createFile(moduleFile, filePath, baseDir, attributes);
+                    if (file != null) {
+                        fileRef.set(file);
+                    }
+                } catch (IncorrectOperationException | IOException var9) {
+                    exceptionRef.set(var9.getMessage());
+                }
+
+            };
+            ApplicationManager.getApplication().runWriteAction(run);
+        }, actionName, null);
+        if (!exceptionRef.isNull()) {
+            Messages.showErrorDialog(exceptionRef.get(), actionName);
+            return null;
+        } else {
+            return fileRef.get();
+        }
+    }
+
+    @Nullable
+    private PsiFile createFile(@NotNull ModuleFileInterface moduleFile, @NotNull String filePath, @NotNull PsiDirectory baseDir, @NotNull Properties attributes) throws IOException, IncorrectOperationException {
+        List<String> path = StringUtil.split(filePath.replace(File.separator, "/"), "/");
+        String fileName = path.get(path.size() - 1);
+        PsiFile fileTemplate = createFileFromTemplate(getTestTemplateManager(), baseDir, moduleFile.getTemplate(), attributes, fileName, moduleFile.getLanguage());
+        if (fileTemplate == null) {
+            throw new IncorrectOperationException("Template not found!");
+        } else {
+            PsiElement file;
+
+            file = baseDir.add(fileTemplate);
+            if (file instanceof PsiFile) {
+                return (PsiFile)file;
+            } else {
+                return null;
+            }
+        }
+    }
+
+    public PsiFile createFileFromTemplate(@NotNull FileTemplateManager templateManager, @NotNull PsiDirectory directory, @NotNull String templateName, @NotNull Properties properties, @NotNull String fileName, @NotNull Language language) throws IOException {
+        FileTemplate fileTemplate = templateManager.getInternalTemplate(templateName);
+        fillDefaultProperties(templateManager, properties, directory);
+        String fileTemplateText = fileTemplate.getText(properties);
+        PsiFile file = PsiFileFactory.getInstance(project).createFileFromText(fileName, language, fileTemplateText, true, false);
+        if (fileTemplate.isReformatCode()) {
+            CodeStyleManager.getInstance(project).reformat(file);
+        }
+
+        return file;
+    }
+
+    public void fillDefaultProperties(@NotNull FileTemplateManager templateManager, @NotNull Properties props, @NotNull PsiDirectory directory) {
+        Properties hardCodedProperties = templateManager.getDefaultProperties();
+        Iterator iterator = hardCodedProperties.keySet().iterator();
+
+        while(iterator.hasNext()) {
+            Object propertyKey = iterator.next();
+            props.setProperty((String)propertyKey, hardCodedProperties.getProperty((String)propertyKey));
+        }
+
+        iterator = DefaultTemplatePropertiesProvider.EP_NAME.getExtensionList().iterator();
+
+        while(iterator.hasNext()) {
+            DefaultTemplatePropertiesProvider provider = (DefaultTemplatePropertiesProvider)iterator.next();
+            provider.fillProperties(directory, props);
+        }
+    }
+
+    public FileTemplateManager getTestTemplateManager() {
+        return FileTemplateManager.getInstance(project);
+    }
+}
diff --git a/src/com/magento/idea/magento2plugin/actions/generation/generator/data/ModuleDirectoriesData.java b/src/com/magento/idea/magento2plugin/actions/generation/generator/data/ModuleDirectoriesData.java
new file mode 100644
index 000000000..1b02cdef5
--- /dev/null
+++ b/src/com/magento/idea/magento2plugin/actions/generation/generator/data/ModuleDirectoriesData.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+package com.magento.idea.magento2plugin.actions.generation.generator.data;
+
+import com.intellij.psi.PsiDirectory;
+import org.jetbrains.annotations.NotNull;
+
+public class ModuleDirectoriesData {
+    @NotNull
+    private final PsiDirectory moduleDirectory;
+    @NotNull
+    private final PsiDirectory moduleEtc;
+
+    public ModuleDirectoriesData(@NotNull PsiDirectory moduleDirectory, @NotNull PsiDirectory moduleEtc) {
+        this.moduleDirectory = moduleDirectory;
+        this.moduleEtc = moduleEtc;
+    }
+
+    @NotNull
+    public PsiDirectory getModuleDirectory() {
+        return moduleDirectory;
+    }
+
+    @NotNull
+    public PsiDirectory getModuleEtcDirectory() {
+        return moduleEtc;
+    }
+}
diff --git a/src/com/magento/idea/magento2plugin/magento/files/ComposerJson.java b/src/com/magento/idea/magento2plugin/magento/files/ComposerJson.java
new file mode 100644
index 000000000..98b05a265
--- /dev/null
+++ b/src/com/magento/idea/magento2plugin/magento/files/ComposerJson.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+package com.magento.idea.magento2plugin.magento.files;
+
+import com.intellij.json.JsonLanguage;
+import com.intellij.lang.Language;
+
+public class ComposerJson implements ModuleFileInterface {
+    public static String FILE_NAME = "composer.json";
+    public static String TEMPLATE = "Magento Module Composer";
+    private static ComposerJson INSTANCE = null;
+
+    public static ComposerJson getInstance() {
+        if (null == INSTANCE) {
+            INSTANCE = new ComposerJson();
+        }
+        return INSTANCE;
+    }
+
+    @Override
+    public String getFileName() {
+        return FILE_NAME;
+    }
+
+    @Override
+    public String getTemplate() {
+        return TEMPLATE;
+    }
+
+    @Override
+    public Language getLanguage() {
+        return JsonLanguage.INSTANCE;
+    }
+}
diff --git a/src/com/magento/idea/magento2plugin/magento/files/ModuleFileInterface.java b/src/com/magento/idea/magento2plugin/magento/files/ModuleFileInterface.java
new file mode 100644
index 000000000..918e3b0f7
--- /dev/null
+++ b/src/com/magento/idea/magento2plugin/magento/files/ModuleFileInterface.java
@@ -0,0 +1,13 @@
+/*
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+package com.magento.idea.magento2plugin.magento.files;
+
+import com.intellij.lang.Language;
+
+public interface ModuleFileInterface {
+    public String getFileName();
+    public String getTemplate();
+    public Language getLanguage();
+}
diff --git a/src/com/magento/idea/magento2plugin/magento/files/ModuleXml.java b/src/com/magento/idea/magento2plugin/magento/files/ModuleXml.java
new file mode 100644
index 000000000..8140ec15d
--- /dev/null
+++ b/src/com/magento/idea/magento2plugin/magento/files/ModuleXml.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+package com.magento.idea.magento2plugin.magento.files;
+
+import com.intellij.lang.Language;
+import com.intellij.lang.xml.XMLLanguage;
+
+public class ModuleXml implements ModuleFileInterface {
+    public static String FILE_NAME = "module.xml";
+    public static String TEMPLATE = "Magento Module XML";
+    private static ModuleXml INSTANCE = null;
+
+    public static ModuleXml getInstance() {
+        if (null == INSTANCE) {
+            INSTANCE = new ModuleXml();
+        }
+        return INSTANCE;
+    }
+
+    @Override
+    public String getFileName() {
+        return FILE_NAME;
+    }
+
+    @Override
+    public String getTemplate() {
+        return TEMPLATE;
+    }
+
+    @Override
+    public Language getLanguage() {
+        return XMLLanguage.INSTANCE;
+    }
+}
diff --git a/src/com/magento/idea/magento2plugin/magento/files/RegistrationPhp.java b/src/com/magento/idea/magento2plugin/magento/files/RegistrationPhp.java
new file mode 100644
index 000000000..715599dc5
--- /dev/null
+++ b/src/com/magento/idea/magento2plugin/magento/files/RegistrationPhp.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+package com.magento.idea.magento2plugin.magento.files;
+
+import com.intellij.lang.Language;
+import com.jetbrains.php.lang.PhpLanguage;
+
+public class RegistrationPhp implements ModuleFileInterface {
+    public static String FILE_NAME = "registration.php";
+    public static String TEMPLATE = "Magento Module Registration Php";
+    private static RegistrationPhp INSTANCE = null;
+
+    public static RegistrationPhp getInstance() {
+        if (null == INSTANCE) {
+            INSTANCE = new RegistrationPhp();
+        }
+        return INSTANCE;
+    }
+
+    @Override
+    public String getFileName() {
+        return FILE_NAME;
+    }
+
+    @Override
+    public String getTemplate() {
+        return TEMPLATE;
+    }
+
+    @Override
+    public Language getLanguage() {
+        return PhpLanguage.INSTANCE;
+    }
+}
diff --git a/src/com/magento/idea/magento2plugin/magento/packages/MagentoPackages.java b/src/com/magento/idea/magento2plugin/magento/packages/MagentoPackages.java
new file mode 100644
index 000000000..4df54a8f3
--- /dev/null
+++ b/src/com/magento/idea/magento2plugin/magento/packages/MagentoPackages.java
@@ -0,0 +1,9 @@
+/*
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+package com.magento.idea.magento2plugin.magento.packages;
+
+public class MagentoPackages {
+    public static String PACKAGES_ROOT = "app/code";
+}
diff --git a/src/com/magento/idea/magento2plugin/util/CamelCaseToHyphen.java b/src/com/magento/idea/magento2plugin/util/CamelCaseToHyphen.java
new file mode 100644
index 000000000..7ee209d2a
--- /dev/null
+++ b/src/com/magento/idea/magento2plugin/util/CamelCaseToHyphen.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+package com.magento.idea.magento2plugin.util;
+
+import java.lang.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class CamelCaseToHyphen {
+    private static CamelCaseToHyphen INSTANCE = null;
+    public static CamelCaseToHyphen getInstance() {
+        if (null == INSTANCE) {
+            INSTANCE = new CamelCaseToHyphen();
+        }
+        return INSTANCE;
+    }
+
+    public String convert(String string) {
+        String regex = "(?=[A-Z][a-z])";
+        String subst = "-";
+
+        Pattern pattern = Pattern.compile(regex);
+        Matcher matcher = pattern.matcher(string);
+
+        String result = matcher.replaceAll(subst);
+
+        return result.toLowerCase().substring(1);
+    }
+}
diff --git a/src/com/magento/idea/magento2plugin/util/Regex.java b/src/com/magento/idea/magento2plugin/util/Regex.java
new file mode 100644
index 000000000..5fe10d817
--- /dev/null
+++ b/src/com/magento/idea/magento2plugin/util/Regex.java
@@ -0,0 +1,11 @@
+/**
+ * Copyright © Dmytro Kvashnin. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+package com.magento.idea.magento2plugin.util;
+
+public class Regex {
+
+    public static final String ALPHANUMERIC
+            = "[a-zA-Z0-9]*";
+}

From 3c81e2503d609fbf23986770a45abb55de1d0a9b Mon Sep 17 00:00:00 2001
From: Vitaliy Boyko <v.boyko@atwix.com>
Date: Tue, 24 Mar 2020 19:55:38 +0200
Subject: [PATCH 2/3] Static fixes

---
 resources/META-INF/plugin.xml                                 | 2 --
 .../dialog/validator/NewMagentoModuleDialogValidator.java     | 4 ++++
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml
index 77c70843e..6780db8c7 100644
--- a/resources/META-INF/plugin.xml
+++ b/resources/META-INF/plugin.xml
@@ -108,7 +108,6 @@
                          groupName="Magento"                                 enabledByDefault="true" level="ERROR"
                          implementationClass="com.magento.idea.magento2plugin.inspections.php.PluginInspection"/>
         <libraryRoot id=".phpstorm.meta.php" path=".phpstorm.meta.php/" runtime="false"/>
-
     </extensions>
 
     <application-components>
@@ -123,5 +122,4 @@
         <!-- Add your actions here -->
     </actions>
 
-
 </idea-plugin>
diff --git a/src/com/magento/idea/magento2plugin/actions/generation/dialog/validator/NewMagentoModuleDialogValidator.java b/src/com/magento/idea/magento2plugin/actions/generation/dialog/validator/NewMagentoModuleDialogValidator.java
index 9c6c05fe3..16d64a9da 100644
--- a/src/com/magento/idea/magento2plugin/actions/generation/dialog/validator/NewMagentoModuleDialogValidator.java
+++ b/src/com/magento/idea/magento2plugin/actions/generation/dialog/validator/NewMagentoModuleDialogValidator.java
@@ -1,3 +1,7 @@
+/*
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
 package com.magento.idea.magento2plugin.actions.generation.dialog.validator;
 
 import com.magento.idea.magento2plugin.actions.generation.dialog.NewMagentoModuleDialog;

From c3a1c7a3b9c68e988d709370fb8782dee43ab9de Mon Sep 17 00:00:00 2001
From: Vitaliy Boyko <v.boyko@atwix.com>
Date: Tue, 24 Mar 2020 20:13:37 +0200
Subject: [PATCH 3/3] Fixed templates

---
 resources/META-INF/plugin.xml                                 | 4 ++++
 resources/fileTemplates/internal/Magento Module Composer.html | 4 ----
 .../internal/Magento Module Registration Php.html             | 4 ----
 resources/fileTemplates/internal/Magento Module XML.html      | 4 ----
 .../{Magento Module XML.xml.ft => Magento Module Xml.xml.ft}  | 0
 .../magento/idea/magento2plugin/magento/files/ModuleXml.java  | 2 +-
 6 files changed, 5 insertions(+), 13 deletions(-)
 delete mode 100644 resources/fileTemplates/internal/Magento Module Composer.html
 delete mode 100644 resources/fileTemplates/internal/Magento Module Registration Php.html
 delete mode 100644 resources/fileTemplates/internal/Magento Module XML.html
 rename resources/fileTemplates/internal/{Magento Module XML.xml.ft => Magento Module Xml.xml.ft} (100%)

diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml
index 6780db8c7..d89d27dbd 100644
--- a/resources/META-INF/plugin.xml
+++ b/resources/META-INF/plugin.xml
@@ -108,6 +108,10 @@
                          groupName="Magento"                                 enabledByDefault="true" level="ERROR"
                          implementationClass="com.magento.idea.magento2plugin.inspections.php.PluginInspection"/>
         <libraryRoot id=".phpstorm.meta.php" path=".phpstorm.meta.php/" runtime="false"/>
+
+        <internalFileTemplate name="Magento Module Composer"/>
+        <internalFileTemplate name="Magento Module Registration Php"/>
+        <internalFileTemplate name="Magento Module Xml"/>
     </extensions>
 
     <application-components>
diff --git a/resources/fileTemplates/internal/Magento Module Composer.html b/resources/fileTemplates/internal/Magento Module Composer.html
deleted file mode 100644
index 6bfc7988e..000000000
--- a/resources/fileTemplates/internal/Magento Module Composer.html	
+++ /dev/null
@@ -1,4 +0,0 @@
-<html>
-<body>
-</body>
-</html>
\ No newline at end of file
diff --git a/resources/fileTemplates/internal/Magento Module Registration Php.html b/resources/fileTemplates/internal/Magento Module Registration Php.html
deleted file mode 100644
index 6bfc7988e..000000000
--- a/resources/fileTemplates/internal/Magento Module Registration Php.html	
+++ /dev/null
@@ -1,4 +0,0 @@
-<html>
-<body>
-</body>
-</html>
\ No newline at end of file
diff --git a/resources/fileTemplates/internal/Magento Module XML.html b/resources/fileTemplates/internal/Magento Module XML.html
deleted file mode 100644
index 6bfc7988e..000000000
--- a/resources/fileTemplates/internal/Magento Module XML.html	
+++ /dev/null
@@ -1,4 +0,0 @@
-<html>
-<body>
-</body>
-</html>
\ No newline at end of file
diff --git a/resources/fileTemplates/internal/Magento Module XML.xml.ft b/resources/fileTemplates/internal/Magento Module Xml.xml.ft
similarity index 100%
rename from resources/fileTemplates/internal/Magento Module XML.xml.ft
rename to resources/fileTemplates/internal/Magento Module Xml.xml.ft
diff --git a/src/com/magento/idea/magento2plugin/magento/files/ModuleXml.java b/src/com/magento/idea/magento2plugin/magento/files/ModuleXml.java
index 8140ec15d..9da3070d6 100644
--- a/src/com/magento/idea/magento2plugin/magento/files/ModuleXml.java
+++ b/src/com/magento/idea/magento2plugin/magento/files/ModuleXml.java
@@ -9,7 +9,7 @@
 
 public class ModuleXml implements ModuleFileInterface {
     public static String FILE_NAME = "module.xml";
-    public static String TEMPLATE = "Magento Module XML";
+    public static String TEMPLATE = "Magento Module Xml";
     private static ModuleXml INSTANCE = null;
 
     public static ModuleXml getInstance() {