├── .gitignore
├── LICENSE.md
├── README.md
├── doc
├── factory_interface.png
├── generate_dialog.png
└── intentions.png
├── intellij-nette-factory-generator.iml
├── resources
├── META-INF
│ └── plugin.xml
└── intentionDescriptions
│ └── GenerateFactoryInterfaceIntention
│ ├── after.GenerateFactoryInterfaceIntention.template
│ ├── before.GenerateFactoryInterfaceIntention.template
│ └── description.html
└── src
└── cz
└── jiripudil
└── intellij
└── nette
└── factoryGenerator
├── codeGeneration
└── FactoryInterfaceGenerator.java
├── intention
└── GenerateFactoryInterfaceIntention.java
└── ui
├── ConstructorParameter.java
├── GenerateFactoryInterfaceDialog.form
└── GenerateFactoryInterfaceDialog.java
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | out/
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016, Jiří Pudil
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
5 |
6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7 |
8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
9 |
10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Nette factory interface generator
2 |
3 | [](https://plugins.jetbrains.com/plugin/9166)
4 | [](https://plugins.jetbrains.com/plugin/9166)
5 |
6 | This plugin allows you to quickly generate factory interface for your components and other classes.
7 |
8 | ## Installation and requirements
9 |
10 | This plugin is written for PhpStorm 10 and above and is compiled for Java 8. You can find it in the Jetbrains plugin repository. Install it from Preferences → Plugins → Browse repositories... and search for it.
11 |
12 |
13 | ## Usage
14 |
15 | With cursor anywhere in a PHP class, press Alt + Enter to open the intentions selection. Choose "Generate service factory interface".
16 |
17 | 
18 |
19 | A dialog will pop up allowing you to change the name of the factory interface and pick which constructor parameters should be enumerated in the factory's `create` method.
20 |
21 | 
22 |
23 | A new PHP file will then be created in the same directory, based on your `PHP File` file template. It will contain the factory interface, residing in the same namespace as the original class.
24 |
25 | 
26 |
--------------------------------------------------------------------------------
/doc/factory_interface.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nette-intellij/intellij-nette-factory-generator/cb624b0d54bff5f4d3b9ef31d539a982432d23dd/doc/factory_interface.png
--------------------------------------------------------------------------------
/doc/generate_dialog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nette-intellij/intellij-nette-factory-generator/cb624b0d54bff5f4d3b9ef31d539a982432d23dd/doc/generate_dialog.png
--------------------------------------------------------------------------------
/doc/intentions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nette-intellij/intellij-nette-factory-generator/cb624b0d54bff5f4d3b9ef31d539a982432d23dd/doc/intentions.png
--------------------------------------------------------------------------------
/intellij-nette-factory-generator.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/resources/META-INF/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 | cz.jiripudil.intellij.nette.factoryGenerator
3 | Nette factory interface generator
4 | 2.0.0
5 | Jiří Pudil
6 |
7 | Github
9 | This plugin allows you to quickly generate factory interface for your components and other classes.
10 | ]]>
11 |
12 | 2.0.0
14 |
15 | - Only made intention available from class name or constructor (see #5)
16 |
17 |
18 | 1.2.0
19 |
20 | - Added checkbox to control whether to open the generated factory in the editor (see #4)
21 |
22 |
23 | 1.1.2
24 |
25 | - Fixed EOF newline (see #3)
26 |
27 |
28 | 1.1.1
29 |
30 | - Fixed compatibility with 2017.1 (see #2)
31 |
32 |
33 | 1.1.0
34 |
35 | - Fixed misplacement of phpdoc for PHP < 7.0 (#1)
36 | - Generated code follows project's indentation and newline settings
37 |
38 |
39 | 1.0.0
40 |
41 | - Initial release.
42 |
43 | ]]>
44 |
45 |
46 |
47 | com.intellij.modules.lang
48 | com.jetbrains.php
49 |
50 |
51 |
52 | cz.jiripudil.intellij.nette.factoryGenerator.codeGeneration.FactoryInterfaceGenerator
53 |
54 |
55 |
56 |
57 |
58 | cz.jiripudil.intellij.nette.factoryGenerator.intention.GenerateFactoryInterfaceIntention
59 | Nette
60 | GenerateFactoryInterfaceIntention
61 |
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/resources/intentionDescriptions/GenerateFactoryInterfaceIntention/after.GenerateFactoryInterfaceIntention.template:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nette-intellij/intellij-nette-factory-generator/cb624b0d54bff5f4d3b9ef31d539a982432d23dd/resources/intentionDescriptions/GenerateFactoryInterfaceIntention/after.GenerateFactoryInterfaceIntention.template
--------------------------------------------------------------------------------
/resources/intentionDescriptions/GenerateFactoryInterfaceIntention/before.GenerateFactoryInterfaceIntention.template:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nette-intellij/intellij-nette-factory-generator/cb624b0d54bff5f4d3b9ef31d539a982432d23dd/resources/intentionDescriptions/GenerateFactoryInterfaceIntention/before.GenerateFactoryInterfaceIntention.template
--------------------------------------------------------------------------------
/resources/intentionDescriptions/GenerateFactoryInterfaceIntention/description.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Generate Nette factory interface
4 |
5 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/factoryGenerator/codeGeneration/FactoryInterfaceGenerator.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.factoryGenerator.codeGeneration;
2 |
3 | import com.intellij.codeStyle.CodeStyleFacade;
4 | import com.intellij.openapi.components.ApplicationComponent;
5 | import com.intellij.openapi.project.Project;
6 | import com.intellij.openapi.util.text.StringUtil;
7 | import com.intellij.psi.PsiFile;
8 | import com.jetbrains.php.config.PhpLanguageFeature;
9 | import com.jetbrains.php.config.PhpLanguageLevel;
10 | import com.jetbrains.php.config.PhpProjectConfigurationFacade;
11 | import com.jetbrains.php.lang.PhpFileType;
12 | import com.jetbrains.php.lang.psi.elements.Parameter;
13 | import com.jetbrains.php.lang.psi.elements.PhpClass;
14 | import com.jetbrains.php.refactoring.PhpFileCreator;
15 | import org.jetbrains.annotations.NotNull;
16 |
17 | import java.util.ArrayList;
18 |
19 | public class FactoryInterfaceGenerator implements ApplicationComponent {
20 | public PsiFile generateFactory(Project project, PsiFile psiFile, PhpClass originalClass, String factoryName, ArrayList parameters) {
21 | String fileName = factoryName + ".php";
22 | if (psiFile.getContainingDirectory().findFile(fileName) != null) {
23 | return null;
24 | }
25 |
26 | PhpLanguageLevel languageLevel = PhpProjectConfigurationFacade.getInstance(project).getLanguageLevel();
27 | String namespace = originalClass.getNamespaceName();
28 | String trimmedNamespace = StringUtil.trimStart(StringUtil.trimEnd(namespace, "\\"), "\\");
29 | StringBuilder contentBuilder = new StringBuilder();
30 |
31 | CodeStyleFacade cs = CodeStyleFacade.getInstance(project);
32 | boolean useTab = cs.useTabCharacter(PhpFileType.INSTANCE);
33 | int indentSize = cs.getIndentSize(PhpFileType.INSTANCE);
34 | String indent = useTab ? "\t" : StringUtil.repeat(" ", indentSize);
35 |
36 | contentBuilder.append(StringUtil.repeat("\n", 2))
37 | .append("namespace ")
38 | .append(trimmedNamespace)
39 | .append(";")
40 | .append(StringUtil.repeat("\n", 3));
41 |
42 | contentBuilder.append("interface ")
43 | .append(factoryName)
44 | .append("\n")
45 | .append("{")
46 | .append(StringUtil.repeat("\n", 2))
47 | .append(indent);
48 |
49 | if ( ! languageLevel.hasFeature(PhpLanguageFeature.RETURN_TYPES)) {
50 | contentBuilder.append("/**")
51 | .append("\n")
52 | .append(indent)
53 | .append(" * @return ")
54 | .append(originalClass.getName())
55 | .append("\n")
56 | .append(indent)
57 | .append(" */")
58 | .append("\n")
59 | .append(indent);
60 | }
61 |
62 | contentBuilder.append("public function create(");
63 |
64 | // factory parameters
65 | for (int index = 0; index < parameters.size(); index++) {
66 | Parameter parameter = parameters.get(index);
67 |
68 | // type hint
69 | if ( ! StringUtil.isEmpty(parameter.getType().toString())) {
70 | contentBuilder.append(parameter.getType().toStringRelativized(namespace))
71 | .append(" ");
72 | }
73 |
74 | // name
75 | contentBuilder.append("$")
76 | .append(parameter.getName());
77 |
78 | // default value
79 | if (parameter.getDefaultValue() != null) {
80 | contentBuilder.append(" = ")
81 | .append(parameter.getDefaultValue().getText());
82 | }
83 |
84 | if (index != parameters.size() - 1) {
85 | contentBuilder.append(", ");
86 | }
87 | }
88 | contentBuilder.append(")");
89 |
90 | if (languageLevel.hasFeature(PhpLanguageFeature.RETURN_TYPES)) {
91 | contentBuilder.append(": ")
92 | .append(originalClass.getName());
93 | }
94 |
95 | contentBuilder.append(";")
96 | .append(StringUtil.repeat("\n", 2))
97 | .append("}")
98 | .append("\n");
99 |
100 | return PhpFileCreator.createPhpFile(
101 | project,
102 | psiFile.getContainingDirectory(),
103 | fileName,
104 | contentBuilder.toString()
105 | );
106 | }
107 |
108 | @Override
109 | public void initComponent() {
110 | }
111 |
112 | @Override
113 | public void disposeComponent() {
114 | }
115 |
116 | @NotNull
117 | @Override
118 | public String getComponentName() {
119 | return "nette-factory-generator.generator";
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/factoryGenerator/intention/GenerateFactoryInterfaceIntention.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.factoryGenerator.intention;
2 |
3 | import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction;
4 | import com.intellij.openapi.editor.Editor;
5 | import com.intellij.openapi.project.Project;
6 | import com.intellij.psi.PsiElement;
7 | import com.intellij.psi.PsiFile;
8 | import com.intellij.util.IncorrectOperationException;
9 | import com.jetbrains.php.PhpWorkaroundUtil;
10 | import com.jetbrains.php.lang.lexer.PhpTokenTypes;
11 | import com.jetbrains.php.lang.psi.PhpFile;
12 | import com.jetbrains.php.lang.psi.PhpPsiUtil;
13 | import com.jetbrains.php.lang.psi.elements.Function;
14 | import com.jetbrains.php.lang.psi.elements.ParameterList;
15 | import com.jetbrains.php.lang.psi.elements.PhpClass;
16 | import com.jetbrains.php.refactoring.PhpRefactoringUtil;
17 | import cz.jiripudil.intellij.nette.factoryGenerator.ui.GenerateFactoryInterfaceDialog;
18 | import org.jetbrains.annotations.Nls;
19 | import org.jetbrains.annotations.NotNull;
20 |
21 | public class GenerateFactoryInterfaceIntention extends PsiElementBaseIntentionAction {
22 | @Nls
23 | @NotNull
24 | @Override
25 | public String getText() {
26 | return "Generate service factory interface";
27 | }
28 |
29 | @NotNull
30 | @Override
31 | public String getFamilyName() {
32 | return getText();
33 | }
34 |
35 | @Override
36 | public boolean startInWriteAction() {
37 | return false;
38 | }
39 |
40 | @Override
41 | public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement psiElement) throws IncorrectOperationException {
42 | PhpClass phpClass = PhpPsiUtil.getParentByCondition(psiElement, PhpClass.INSTANCEOF);
43 |
44 | if (phpClass == null || editor == null) {
45 | return;
46 | }
47 |
48 | GenerateFactoryInterfaceDialog.create(editor.getComponent(), project, psiElement.getContainingFile(), phpClass, editor);
49 | }
50 |
51 | @Override
52 | public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement psiElement) {
53 | PsiFile file = psiElement.getContainingFile();
54 | return file instanceof PhpFile
55 | && PhpWorkaroundUtil.isIntentionAvailable(psiElement)
56 | && (isInvokedOnConstructor(psiElement) || isInvokedOnClass(psiElement));
57 |
58 | }
59 |
60 | private boolean isInvokedOnConstructor(@NotNull PsiElement psiElement) {
61 | PsiElement parameters = PhpPsiUtil.getParentByCondition(psiElement, false, ParameterList.INSTANCEOF);
62 | if (parameters != null) {
63 | return PhpRefactoringUtil.isElementConstructor(parameters.getParent());
64 |
65 | } else {
66 | PsiElement parent = psiElement.getParent();
67 | return PhpRefactoringUtil.isElementConstructor(parent)
68 | && (
69 | ((Function) parent).getNameIdentifier() == psiElement
70 | || PhpPsiUtil.isOfType(psiElement, PhpTokenTypes.kwFUNCTION)
71 | || PhpPsiUtil.isOfType(psiElement, PhpTokenTypes.chLPAREN)
72 | || PhpPsiUtil.isOfType(psiElement, PhpTokenTypes.chRPAREN)
73 | );
74 |
75 | }
76 | }
77 |
78 | private boolean isInvokedOnClass(@NotNull PsiElement psiElement) {
79 | PhpClass phpClass = PhpPsiUtil.getParentByCondition(psiElement, PhpClass.INSTANCEOF);
80 | return phpClass != null
81 | && (
82 | phpClass.getNameIdentifier() == psiElement
83 | || PhpPsiUtil.isOfType(psiElement, PhpTokenTypes.kwCLASS)
84 | );
85 |
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/factoryGenerator/ui/ConstructorParameter.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.factoryGenerator.ui;
2 |
3 | import com.jetbrains.php.lang.psi.elements.Parameter;
4 |
5 | import java.io.Serializable;
6 |
7 | class ConstructorParameter implements Serializable {
8 | private Parameter parameter;
9 |
10 | ConstructorParameter(Parameter parameter) {
11 | this.parameter = parameter;
12 | }
13 |
14 | Parameter getParameter() {
15 | return this.parameter;
16 | }
17 |
18 | public String toString() {
19 | return this.parameter.getType().toString() + " $" + this.parameter.getName();
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/factoryGenerator/ui/GenerateFactoryInterfaceDialog.form:
--------------------------------------------------------------------------------
1 |
2 |
113 |
--------------------------------------------------------------------------------
/src/cz/jiripudil/intellij/nette/factoryGenerator/ui/GenerateFactoryInterfaceDialog.java:
--------------------------------------------------------------------------------
1 | package cz.jiripudil.intellij.nette.factoryGenerator.ui;
2 |
3 | import com.intellij.openapi.application.ApplicationManager;
4 | import com.intellij.openapi.editor.Editor;
5 | import com.intellij.openapi.fileEditor.FileEditorManager;
6 | import com.intellij.openapi.fileEditor.OpenFileDescriptor;
7 | import com.intellij.openapi.project.Project;
8 | import com.intellij.psi.PsiFile;
9 | import com.intellij.ui.CollectionListModel;
10 | import com.intellij.ui.components.JBList;
11 | import com.intellij.ui.components.JBTextField;
12 | import com.jetbrains.php.lang.psi.elements.Method;
13 | import com.jetbrains.php.lang.psi.elements.Parameter;
14 | import com.jetbrains.php.lang.psi.elements.PhpClass;
15 | import cz.jiripudil.intellij.nette.factoryGenerator.codeGeneration.FactoryInterfaceGenerator;
16 | import org.jetbrains.annotations.NotNull;
17 | import org.jetbrains.annotations.Nullable;
18 |
19 | import javax.swing.*;
20 | import java.awt.*;
21 | import java.awt.event.*;
22 | import java.util.*;
23 |
24 | public class GenerateFactoryInterfaceDialog extends JDialog {
25 | private JPanel contentPane;
26 | private JButton buttonOK;
27 | private JButton buttonCancel;
28 | private JBTextField interfaceName;
29 | private JBList factoryParams;
30 | private JCheckBox openFile;
31 |
32 | @NotNull private Project project;
33 | @Nullable private PsiFile psiFile;
34 | @Nullable private Editor editor;
35 | @NotNull private PhpClass originalClass;
36 |
37 | private GenerateFactoryInterfaceDialog(@NotNull final Project project, @Nullable PsiFile psiFile, @Nullable Editor editor, @NotNull PhpClass originalClass) {
38 | this.project = project;
39 | this.psiFile = psiFile;
40 | this.editor = editor;
41 | this.originalClass = originalClass;
42 |
43 | setContentPane(contentPane);
44 | setModal(true);
45 | getRootPane().setDefaultButton(buttonOK);
46 |
47 | buttonOK.addActionListener(e -> generateFactory());
48 | buttonCancel.addActionListener(e -> dispose());
49 |
50 | interfaceName.setText("I" + originalClass.getName() + "Factory");
51 | initFactoryParams();
52 |
53 | setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
54 | addWindowListener(new WindowAdapter() {
55 | public void windowClosing(WindowEvent e) {
56 | dispose();
57 | }
58 | });
59 |
60 | contentPane.registerKeyboardAction(e -> dispose(), KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
61 | }
62 |
63 | public static GenerateFactoryInterfaceDialog create(@NotNull Component component, @NotNull Project project, @NotNull PsiFile psiFile, @NotNull PhpClass phpClass, @Nullable Editor editor) {
64 | GenerateFactoryInterfaceDialog dialog = new GenerateFactoryInterfaceDialog(project, psiFile, editor, phpClass);
65 | dialog.setTitle("Nette factory interface generator");
66 | dialog.pack();
67 |
68 | dialog.setMinimumSize(new Dimension(550, 250));
69 | dialog.setLocationRelativeTo(component);
70 | dialog.setVisible(true);
71 |
72 | return dialog;
73 | }
74 |
75 | private void createUIComponents() {
76 | interfaceName = new JBTextField();
77 | factoryParams = new JBList<>();
78 | }
79 |
80 | private void initFactoryParams() {
81 | Method constructor = originalClass.getConstructor();
82 | java.util.List list = new ArrayList<>();
83 | if (constructor != null) {
84 | for (Parameter parameter : constructor.getParameters()) {
85 | list.add(new ConstructorParameter(parameter));
86 | }
87 | }
88 |
89 | ListModel model = new CollectionListModel<>(list);
90 | factoryParams.setModel(model);
91 | factoryParams.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
92 | }
93 |
94 | private void generateFactory() {
95 | String name = interfaceName.getText();
96 | ArrayList parameters = new ArrayList<>();
97 |
98 | ListModel model = factoryParams.getModel();
99 | for (int index : factoryParams.getSelectedIndices()) {
100 | ConstructorParameter parameter = model.getElementAt(index);
101 | parameters.add(parameter.getParameter());
102 | }
103 |
104 | FactoryInterfaceGenerator generator = ApplicationManager.getApplication().getComponent(FactoryInterfaceGenerator.class);
105 | PsiFile file = generator.generateFactory(project, psiFile, originalClass, name, parameters);
106 |
107 | if (openFile.isSelected()) {
108 | OpenFileDescriptor descriptor = new OpenFileDescriptor(project, file.getVirtualFile());
109 | FileEditorManager.getInstance(project).openEditor(descriptor, true);
110 | }
111 |
112 | dispose();
113 | }
114 | }
115 |
--------------------------------------------------------------------------------