├── .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 | [![Version](http://phpstorm.espend.de/badge/9166/version)](https://plugins.jetbrains.com/plugin/9166) 4 | [![Downloads](http://phpstorm.espend.de/badge/9166/downloads)](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 | ![Intention menu](doc/intentions.png) 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 | ![Generate factory interface dialog](doc/generate_dialog.png) 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 | ![Generated factory interface](doc/factory_interface.png) 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 | 17 | 18 |

1.2.0

19 | 22 | 23 |

1.1.2

24 | 27 | 28 |

1.1.1

29 | 32 | 33 |

1.1.0

34 | 38 | 39 |

1.0.0

40 | 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 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 |
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 | --------------------------------------------------------------------------------