Skip to content

Commit

Permalink
Merge pull request #2319 from Haehnchen/feature/new-command-action
Browse files Browse the repository at this point in the history
migrate new Symfony command action and provide file template guessing
  • Loading branch information
Haehnchen authored Mar 31, 2024
2 parents 702cf39 + 8b3a1af commit 4398d69
Show file tree
Hide file tree
Showing 8 changed files with 277 additions and 70 deletions.
Original file line number Diff line number Diff line change
@@ -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<String> strings = PhpNamespaceCompositeProvider.INSTANCE.suggestNamespaces(directory);
if (strings.isEmpty()) {
JOptionPane.showMessageDialog(null, "No namespace found");
return;
}

ApplicationManager.getApplication().runWriteAction(() -> {
HashMap<String, String> 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()));
}
}
}
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>
*/
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<String> strings = PhpNamespaceCompositeProvider.INSTANCE.suggestNamespaces(psiDirectory).stream().filter(s -> !s.isBlank()).toList();
if (!strings.isEmpty()) {
Collection<SymfonyCommand> commands = SymfonyCommandUtil.getCommands(psiDirectory.getProject());

LinkedHashMap<String, Integer> 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<Map.Entry<String, Integer>> 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";
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String> vars) {
String COMPILER_TEMPLATE = "/fileTemplates/" + template + ".php";
String fileTemplateContent = getFileTemplateContent(COMPILER_TEMPLATE);
if(fileTemplateContent == null) {
throw new RuntimeException("Template content error");
}

for (Map.Entry<String, String> 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 {

Expand Down
15 changes: 10 additions & 5 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -776,18 +776,23 @@
<add-to-group group-id="GenerateGroup" anchor="last" />
</action>

<group id="Symfony2Group" text="Symfony" popup="false">
<group id="Symfony2GroupService" class="com.intellij.ide.actions.NonTrivialActionGroup" text="Service" popup="true" icon="SymfonyIcons.Symfony">
<group id="SymfonyGroup" text="Symfony" popup="false">
<group id="SymfonyNewFile" class="com.intellij.ide.actions.NonTrivialActionGroup" text="Symfony" popup="true" icon="SymfonyIcons.Symfony">
<action id="SymfonyNewCommandAction" class="fr.adrienbrault.idea.symfony2plugin.action.NewCommandAction"/>
<separator/>
<action id="Symfony2NewXmlService" class="fr.adrienbrault.idea.symfony2plugin.action.NewXmlServiceAction"/>
<action id="Symfony2NewYamlService" class="fr.adrienbrault.idea.symfony2plugin.action.NewYamlServiceAction"/>
</group>
<separator/>
<action id="Symfony2NewControllerService" class="fr.adrienbrault.idea.symfony2plugin.action.bundle.NewBundleControllerAction"/>
<action id="SymfonyBundleCompilerPass" class="fr.adrienbrault.idea.symfony2plugin.action.bundle.NewBundleCompilerPass"/>
<action id="SymfonyBundleFormAction" class="fr.adrienbrault.idea.symfony2plugin.action.bundle.NewBundleFormAction"/>
<action id="SymfonyBundleTwigExtensionAction" class="fr.adrienbrault.idea.symfony2plugin.action.bundle.NewBundleTwigExtensionAction"/>
<action id="SymfonyBundleCommandAction" class="fr.adrienbrault.idea.symfony2plugin.action.bundle.NewBundleCommandAction"/>
<add-to-group group-id="NewGroup" anchor="last"/>
<add-to-group group-id="NewGroup" anchor="after" relative-to-action="PhpNewGroup"/>
</group>

<group id="SymfonyGroupShortcut" text="Symfony" popup="false">
<action id="SymfonyNewCommandActionShortcut" class="fr.adrienbrault.idea.symfony2plugin.action.NewCommandAction$Shortcut"/>
<add-to-group group-id="NewGroup" anchor="before" relative-to-action="SymfonyGroup" />
</group>

<action id="SymfonyWebTestCaseGenerator" class="fr.adrienbrault.idea.symfony2plugin.action.bundle.WebTestCaseGeneratorAction">
Expand Down
4 changes: 2 additions & 2 deletions src/main/resources/fileTemplates/command.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace {{ ns }};
namespace {{ namespace }};

use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputInterface;
Expand All @@ -14,7 +14,7 @@ class {{ class }} extends ContainerAwareCommand
protected function configure()
{
$this
->setName('{{ name }}')
->setName('{{ command_name }}')
->setDescription('Hello PhpStorm');
}

Expand Down
19 changes: 19 additions & 0 deletions src/main/resources/fileTemplates/command_attributes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace {{ namespace }};

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(name: '{{ command_name }}', description: 'Hello PhpStorm')]
class {{ class }} extends Command
{
protected function execute(InputInterface $input, OutputInterface $output): int
{
return Command::SUCCESS;
}
}
27 changes: 27 additions & 0 deletions src/main/resources/fileTemplates/command_configure.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace {{ namespace }};

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class {{ class }} extends Command
{
/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->setName('{{ command_name }}')
->setDescription('Hello PhpStorm');
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
return Command::SUCCESS;
}
}

0 comments on commit 4398d69

Please sign in to comment.