diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/action/NewCommandAction.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/action/NewCommandAction.java new file mode 100644 index 000000000..f30aed61b --- /dev/null +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/action/NewCommandAction.java @@ -0,0 +1,101 @@ +package fr.adrienbrault.idea.symfony2plugin.action; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.fileEditor.OpenFileDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiElement; +import com.jetbrains.php.refactoring.PhpNameUtil; +import com.jetbrains.php.roots.PhpNamespaceCompositeProvider; +import fr.adrienbrault.idea.symfony2plugin.Symfony2Icons; +import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent; +import fr.adrienbrault.idea.symfony2plugin.util.psi.PhpBundleFileFactory; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.util.HashMap; +import java.util.List; + +public class NewCommandAction extends AbstractProjectDumbAwareAction { + public NewCommandAction() { + super("Command", "Create Command Class", Symfony2Icons.SYMFONY); + } + + public void update(AnActionEvent event) { + this.setStatus(event, false); + Project project = getEventProject(event); + if (!Symfony2ProjectComponent.isEnabled(project)) { + return; + } + + if (NewFileActionUtil.getSelectedDirectoryFromAction(event) != null) { + this.setStatus(event, true); + } + } + + @Override + public void actionPerformed(@NotNull AnActionEvent event) { + PsiDirectory directory = NewFileActionUtil.getSelectedDirectoryFromAction(event); + if (directory == null) { + return; + } + + Project project = getEventProject(event); + String className = Messages.showInputDialog(project, "New class name:", "New File", Symfony2Icons.SYMFONY); + if (StringUtils.isBlank(className)) { + return; + } + + if (!PhpNameUtil.isValidClassName(className)) { + JOptionPane.showMessageDialog(null, "Invalid class name"); + return; + } + + List strings = PhpNamespaceCompositeProvider.INSTANCE.suggestNamespaces(directory); + if (strings.isEmpty()) { + JOptionPane.showMessageDialog(null, "No namespace found"); + return; + } + + ApplicationManager.getApplication().runWriteAction(() -> { + HashMap hashMap = new HashMap<>() {{ + String clazz = className; + if (className.endsWith("Command")) { + clazz = className.substring(0, "Command".length()); + } + + String prefix = NewFileActionUtil.getCommandPrefix(directory); + + put("class", className); + put("namespace", strings.get(0)); + put("command_name", prefix + ":" + fr.adrienbrault.idea.symfony2plugin.util.StringUtils.underscore(clazz)); + }}; + + PsiElement commandAttributes = PhpBundleFileFactory.createFile( + project, + directory.getVirtualFile(), + NewFileActionUtil.guessCommandTemplateType(project), + className, + hashMap + ); + + new OpenFileDescriptor(project, commandAttributes.getContainingFile().getVirtualFile(), 0).navigate(true); + }); + } + + public static class Shortcut extends NewCommandAction { + @Override + public void update(AnActionEvent event) { + Project project = getEventProject(event); + if (!Symfony2ProjectComponent.isEnabled(project)) { + return; + } + + PsiDirectory directory = NewFileActionUtil.getSelectedDirectoryFromAction(event); + this.setStatus(event, directory != null && "Command".equals(directory.getName())); + } + } +} diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/action/NewFileActionUtil.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/action/NewFileActionUtil.java new file mode 100644 index 000000000..1840af577 --- /dev/null +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/action/NewFileActionUtil.java @@ -0,0 +1,102 @@ +package fr.adrienbrault.idea.symfony2plugin.action; + +import com.intellij.ide.IdeView; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.LangDataKeys; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiDirectory; +import com.jetbrains.php.roots.PhpNamespaceCompositeProvider; +import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil; +import fr.adrienbrault.idea.symfony2plugin.util.StringUtils; +import fr.adrienbrault.idea.symfony2plugin.util.SymfonyBundleUtil; +import fr.adrienbrault.idea.symfony2plugin.util.SymfonyCommandUtil; +import fr.adrienbrault.idea.symfony2plugin.util.dict.SymfonyBundle; +import fr.adrienbrault.idea.symfony2plugin.util.dict.SymfonyCommand; +import org.jetbrains.annotations.NotNull; + +import java.util.*; + +/** + * @author Daniel Espendiller + */ +public class NewFileActionUtil { + + public static PsiDirectory getSelectedDirectoryFromAction(@NotNull AnActionEvent event) { + DataContext dataContext = event.getDataContext(); + IdeView view = LangDataKeys.IDE_VIEW.getData(dataContext); + if (view == null) { + return null; + } + + PsiDirectory @NotNull [] directories = view.getDirectories(); + return directories.length == 0 ? null : directories[0]; + + } + + public static String guessCommandTemplateType(@NotNull Project project) { + if (PhpElementsUtil.getClassInterface(project, "\\Symfony\\Component\\Console\\Attribute\\AsCommand") != null) { + return "command_attributes"; + } + + boolean isDefaultName = PhpElementsUtil.getClassesInterface(project, "\\Symfony\\Component\\Console\\Command\\Command") + .stream() + .anyMatch(phpClass -> phpClass.findFieldByName("defaultName", false) != null); + + if (isDefaultName) { + return "command_property"; + } + + return "command_configure"; + } + + public static String getCommandPrefix(@NotNull PsiDirectory psiDirectory) { + List strings = PhpNamespaceCompositeProvider.INSTANCE.suggestNamespaces(psiDirectory).stream().filter(s -> !s.isBlank()).toList(); + if (!strings.isEmpty()) { + Collection commands = SymfonyCommandUtil.getCommands(psiDirectory.getProject()); + + LinkedHashMap sortedMap = new LinkedHashMap<>(); + for (String namespace : strings) { + namespace = "\\" + org.apache.commons.lang3.StringUtils.strip(namespace, "\\") + "\\"; + + for (SymfonyCommand command : commands) { + if (command.getPhpClass().getFQN().startsWith(namespace)) { + String name = command.getName(); + int i = name.indexOf(":"); + if (i > 0) { + String key = name.substring(0, i); + sortedMap.put(key, sortedMap.getOrDefault(key, 0)); + } + } + } + } + + if (!sortedMap.isEmpty()) { + List> list = new ArrayList<>(sortedMap.entrySet() + .stream() + .sorted(Map.Entry.comparingByValue()) + .toList()); + + Collections.reverse(list); + + return list.get(0).getKey(); + } + } + + for (SymfonyBundle bundle : new SymfonyBundleUtil(psiDirectory.getProject()).getBundles()) { + if (bundle.isInBundle(psiDirectory.getVirtualFile())) { + String name = bundle.getName(); + if (name.endsWith("Bundle")) { + name = name.substring(0, name.length() - "Bundle".length()); + } + + String underscore = StringUtils.underscore(name); + if (!underscore.isBlank()) { + return underscore; + } + } + } + + return "app"; + } +} diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/action/bundle/NewBundleCommandAction.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/action/bundle/NewBundleCommandAction.java deleted file mode 100644 index 8ddf76593..000000000 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/action/bundle/NewBundleCommandAction.java +++ /dev/null @@ -1,63 +0,0 @@ -package fr.adrienbrault.idea.symfony2plugin.action.bundle; - -import com.intellij.openapi.application.Result; -import com.intellij.openapi.command.WriteCommandAction; -import com.intellij.openapi.fileEditor.OpenFileDescriptor; -import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiElement; -import com.jetbrains.php.lang.psi.elements.PhpClass; -import fr.adrienbrault.idea.symfony2plugin.Symfony2Icons; -import fr.adrienbrault.idea.symfony2plugin.util.StringUtils; -import fr.adrienbrault.idea.symfony2plugin.util.psi.PhpBundleFileFactory; -import org.jetbrains.annotations.NotNull; - -import javax.swing.*; -import java.util.HashMap; - -/** - * @author Daniel Espendiller - */ -public class NewBundleCommandAction extends NewBundleFileActionAbstract { - - public NewBundleCommandAction() { - super("Command", "Create Command Class", Symfony2Icons.SYMFONY); - } - - protected void write(@NotNull final Project project, @NotNull final PhpClass phpClass, @NotNull String className) { - - if(!className.endsWith("Command")) { - className += "Command"; - } - - final String finalClassName = className; - new WriteCommandAction(project) { - @Override - protected void run(@NotNull Result result) throws Throwable { - - PsiElement bundleFile = null; - try { - - bundleFile = PhpBundleFileFactory.createBundleFile(phpClass, "command", "Command\\" + finalClassName, new HashMap<>() {{ - String name = phpClass.getName(); - if (name.endsWith("Bundle")) { - name = name.substring(0, name.length() - "Bundle".length()); - } - put("name", StringUtils.underscore(name) + ":" + StringUtils.underscore(finalClassName)); - }}); - - } catch (Exception e) { - JOptionPane.showMessageDialog(null, "Error:" + e.getMessage()); - return; - } - - new OpenFileDescriptor(getProject(), bundleFile.getContainingFile().getVirtualFile(), 0).navigate(true); - } - - @Override - public String getGroupID() { - return "Create Command"; - } - }.execute(); - } - -} diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/util/psi/PhpBundleFileFactory.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/util/psi/PhpBundleFileFactory.java index 1b6eb8304..1b5d788ca 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/util/psi/PhpBundleFileFactory.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/util/psi/PhpBundleFileFactory.java @@ -97,6 +97,22 @@ public static PsiElement createBundleFile(@NotNull PhpClass bundleClass, @NotNul return PsiDirectoryFactory.getInstance(project).createDirectory(compilerDirectory).add(fileFromText); } + public static PsiElement createFile(@NotNull Project project, @NotNull VirtualFile parentDirectory, @NotNull String template, @NotNull String filename, @NotNull Map vars) { + String COMPILER_TEMPLATE = "/fileTemplates/" + template + ".php"; + String fileTemplateContent = getFileTemplateContent(COMPILER_TEMPLATE); + if(fileTemplateContent == null) { + throw new RuntimeException("Template content error"); + } + + for (Map.Entry entry : vars.entrySet()) { + fileTemplateContent = fileTemplateContent.replace("{{ " + entry.getKey() + " }}", entry.getValue()); + } + + PsiFile fileFromText = PsiFileFactory.getInstance(project).createFileFromText(filename + ".php", PhpFileType.INSTANCE, fileTemplateContent); + CodeStyleManager.getInstance(project).reformat(fileFromText); + return PsiDirectoryFactory.getInstance(project).createDirectory(parentDirectory).add(fileFromText); + } + @NotNull public static PsiElement createCompilerPass(@NotNull PhpClass bundleClass, @NotNull String className) throws Exception { diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 23df38580..f72351079 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -776,18 +776,23 @@ - - + + + + - - - + + + + + + diff --git a/src/main/resources/fileTemplates/command.php b/src/main/resources/fileTemplates/command.php index 7ceaa7867..dc880f64d 100644 --- a/src/main/resources/fileTemplates/command.php +++ b/src/main/resources/fileTemplates/command.php @@ -1,6 +1,6 @@ setName('{{ name }}') + ->setName('{{ command_name }}') ->setDescription('Hello PhpStorm'); } diff --git a/src/main/resources/fileTemplates/command_attributes.php b/src/main/resources/fileTemplates/command_attributes.php new file mode 100644 index 000000000..33791d9ec --- /dev/null +++ b/src/main/resources/fileTemplates/command_attributes.php @@ -0,0 +1,19 @@ +setName('{{ command_name }}') + ->setDescription('Hello PhpStorm'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + return Command::SUCCESS; + } +}