diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcActionBase.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcActionBase.java new file mode 100644 index 000000000000..5fdf4d9eae65 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcActionBase.java @@ -0,0 +1,88 @@ +package org.jetbrains.plugins.groovy.mvc; + +import com.intellij.openapi.actionSystem.ActionPlaces; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.DataKeys; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.project.DumbAwareAction; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public abstract class MvcActionBase extends DumbAwareAction { + + protected abstract void actionPerformed(@NotNull AnActionEvent e, @NotNull Module module, @NotNull MvcFramework framework); + + @Override + public final void actionPerformed(AnActionEvent e) { + Pair pair = guessFramework(e); + if (pair != null && isSupportFramework(pair.getFirst())) { + actionPerformed(e, pair.getSecond(), pair.getFirst()); + } + } + + protected boolean isSupportFramework(@NotNull MvcFramework framework) { + return true; + } + + @Nullable + public static Pair guessFramework(AnActionEvent event) { + final Module module = event.getData(event.getPlace() == ActionPlaces.MAIN_MENU ? DataKeys.MODULE : DataKeys.MODULE_CONTEXT); + + if (module != null) { + MvcFramework commonPluginModuleFramework = MvcFramework.findCommonPluginModuleFramework(module); + + if (commonPluginModuleFramework != null) { + for (Module mod : ModuleManager.getInstance(module.getProject()).getModules()) { + if (commonPluginModuleFramework.getCommonPluginsModuleName(mod).equals(module.getName())) { + if (commonPluginModuleFramework.hasSupport(mod)) { + return new Pair(commonPluginModuleFramework, mod); + } + + return null; + } + } + } + + MvcFramework framework = MvcModuleStructureSynchronizer.getFramework(module); + if (framework != null) { + return Pair.create(framework, module); + } + } + + final Project project = DataKeys.PROJECT.getData(event.getDataContext()); + if (project == null) { + return null; + } + + Pair result = null; + for (Module mod : ModuleManager.getInstance(project).getModules()) { + final MvcFramework framework = MvcModuleStructureSynchronizer.getFramework(mod); + if (framework != null) { + if (result != null) { + return null; + } + result = Pair.create(framework, mod); + } + } + + return result; + } + + public final void update(AnActionEvent event) { + Pair pair = guessFramework(event); + if (pair != null && isSupportFramework(pair.getFirst())) { + event.getPresentation().setVisible(true); + updateView(event, pair.getFirst(), pair.getSecond()); + } + else { + event.getPresentation().setVisible(false); + } + } + + protected void updateView(AnActionEvent event, @NotNull MvcFramework framework, @NotNull Module module) { + + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcRunTarget.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcRunTarget.java new file mode 100644 index 000000000000..c4b0df9d12a8 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcRunTarget.java @@ -0,0 +1,33 @@ +package org.jetbrains.plugins.groovy.mvc; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.util.Pair; +import org.jetbrains.annotations.NotNull; + +/** + * @author ilyas + */ +public class MvcRunTarget extends MvcActionBase { + + @Override + protected void actionPerformed(@NotNull AnActionEvent e, @NotNull Module module, @NotNull MvcFramework framework) { + MvcRunTargetDialog dialog = new MvcRunTargetDialog(module, framework); + dialog.show(); + if (!dialog.isOK()) { + return; + } + + Module selectedModule = dialog.getSelectedModule(); + + String[] targetArgs = dialog.getTargetArguments(); + + Pair parsedCmd = MvcFramework.parsedCmd(targetArgs); + + ProcessBuilder pb = framework.createCommandAndShowErrors(dialog.getVmOptions(), selectedModule, parsedCmd.first, parsedCmd.second); + if (pb == null) return; + + MvcConsole.getInstance(selectedModule.getProject()).executeProcess(selectedModule, pb, null, false); + } + +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcRunTargetDialog.form b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcRunTargetDialog.form new file mode 100644 index 000000000000..7081d8c7f498 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcRunTargetDialog.form @@ -0,0 +1,89 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcRunTargetDialog.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcRunTargetDialog.java new file mode 100644 index 000000000000..40467e02618b --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcRunTargetDialog.java @@ -0,0 +1,212 @@ +package org.jetbrains.plugins.groovy.mvc; + +import com.intellij.codeInsight.completion.CompletionResultSet; +import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.openapi.editor.event.DocumentAdapter; +import com.intellij.openapi.editor.event.DocumentEvent; +import com.intellij.openapi.fileTypes.PlainTextFileType; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.ui.ComboBox; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.ui.*; +import com.intellij.util.ArrayUtil; +import com.intellij.util.SystemProperties; +import com.intellij.util.TextFieldCompletionProvider; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.mvc.util.ModuleCellRenderer; +import org.jetbrains.plugins.groovy.refactoring.GroovyNamesUtil; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class MvcRunTargetDialog extends DialogWrapper { + + private JPanel contentPane; + private JLabel myTargetLabel; + @SuppressWarnings({"UnusedDeclaration"}) private JPanel myFakePanel; + private JTextField myVmOptionsField; + private JComboBox myModuleBox; + private JLabel myModuleLabel; + private JLabel myVmOptionLabel; + private ComboBox myTargetField; + private Module myModule; + private final MvcFramework myFramework; + + private Set myCompletionVariantCache = null; + + public MvcRunTargetDialog(@NotNull Module module, MvcFramework framework) { + super(module.getProject(), true); + myModule = module; + myFramework = framework; + setTitle("Run " + framework.getDisplayName() + " target"); + setUpDialog(); + setModal(true); + init(); + } + + private void setUpDialog() { + myTargetLabel.setLabelFor(myTargetField); + myTargetField.setFocusable(true); + + myVmOptionLabel.setLabelFor(myVmOptionsField); + myVmOptionsField.setText(MvcRunTargetHistoryService.getInstance().getVmOptions()); + + List mvcModules = new ArrayList(); + for (Module module : ModuleManager.getInstance(myModule.getProject()).getModules()) { + if (module == myModule || MvcModuleStructureSynchronizer.getFramework(module) != null) { + mvcModules.add(module); + } + } + + assert mvcModules.contains(myModule); + + myModuleLabel.setLabelFor(myModuleBox); + myModuleBox.setModel(new CollectionComboBoxModel(mvcModules, myModule)); + myModuleBox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + myModule = (Module)myModuleBox.getSelectedItem(); + myCompletionVariantCache = null; + } + }); + + myModuleBox.setRenderer(new ModuleCellRenderer()); + } + + @NotNull + public Module getSelectedModule() { + return myModule; + } + + @Override + protected void doOKAction() { + super.doOKAction(); + MvcRunTargetHistoryService.getInstance().addCommand(getSelectedText(), getVmOptions()); + } + + public String getVmOptions() { + return myVmOptionsField.getText(); + } + + public String getSelectedText() { + return (String)myTargetField.getEditor().getItem(); + } + + public String[] getTargetArguments() { + String text = getSelectedText(); + Iterable iterable = StringUtil.tokenize(text, " "); + ArrayList args = new ArrayList(); + for (String s : iterable) { + args.add(s); + } + return ArrayUtil.toStringArray(args); + } + + protected JComponent createCenterPanel() { + return contentPane; + } + + public JComponent getPreferredFocusedComponent() { + return myTargetField; + } + + private void createUIComponents() { + myTargetField = new ComboBox(MvcRunTargetHistoryService.getInstance().getHistory(), -1); + myTargetField.setLightWeightPopupEnabled(false); + + EditorComboBoxEditor editor = new StringComboboxEditor(myModule.getProject(), PlainTextFileType.INSTANCE, myTargetField); + myTargetField.setRenderer(new EditorComboBoxRenderer(editor)); + + myTargetField.setEditable(true); + myTargetField.setEditor(editor); + + EditorTextField editorTextField = (EditorTextField)editor.getEditorComponent(); + + myFakePanel = new JPanel(new BorderLayout()); + myFakePanel.add(myTargetField, BorderLayout.CENTER); + + new TextFieldCompletionProvider() { + + @NotNull + @Override + protected String getPrefix(@NotNull String currentTextPrefix) { + return currentTextPrefix.substring(currentTextPrefix.lastIndexOf(' ') + 1); + } + + @Override + protected void addCompletionVariants(@NotNull String text, int offset, @NotNull String prefix, @NotNull CompletionResultSet result) { + if (myCompletionVariantCache == null) { + myCompletionVariantCache = getAllTargetNames(myModule); + } + + for (String completionVariant : myCompletionVariantCache) { + result.addElement(LookupElementBuilder.create(completionVariant)); + } + } + }.apply(editorTextField); + + editorTextField.getDocument().addDocumentListener(new DocumentAdapter() { + @Override + public void documentChanged(DocumentEvent e) { + setOKActionEnabled(!StringUtil.isEmptyOrSpaces(e.getDocument().getText())); + } + }); + setOKActionEnabled(false); + } + + private Set getAllTargetNames(@NotNull Module module) { + final Set result = new HashSet(); + + MvcFramework.addAvailableSystemScripts(result, module); + + final VirtualFile root = myFramework.findAppRoot(module); + if (root != null) { + MvcFramework.addAvailableScripts(result, root); + } + + for (VirtualFile pluginRoot : myFramework.getAllPluginRoots(module, false)) { + MvcFramework.addAvailableScripts(result, pluginRoot); + } + + addScriptsFromUserHome(result); + + return result; + } + + private static void addScriptsFromUserHome(Set result) { + String userHome = SystemProperties.getUserHome(); + if (userHome == null) return; + + File scriptFolder = new File(userHome, ".grails/scripts"); + + File[] files = scriptFolder.listFiles(); + + if (files == null) return; + + for (File file : files) { + if (isScriptFile(file)) { + String name = file.getName(); + int idx = name.lastIndexOf('.'); + if (idx != -1) { + name = name.substring(0, idx); + } + + result.add(GroovyNamesUtil.camelToSnake(name)); + } + } + } + + public static boolean isScriptFile(File file) { + return file.isFile() && MvcFramework.isScriptFileName(file.getName()); + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcRunTargetHistoryService.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcRunTargetHistoryService.java new file mode 100644 index 000000000000..40ffea970148 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcRunTargetHistoryService.java @@ -0,0 +1,92 @@ +package org.jetbrains.plugins.groovy.mvc; + +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import com.intellij.util.ArrayUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.LinkedList; + +/** + * @author Sergey Evdokimov + */ +@State( + name = "mvcRunTargetHistory", + storages = @Storage( + id ="other", + file = "$APP_CONFIG$/other.xml" + ) +) +public class MvcRunTargetHistoryService implements PersistentStateComponent { + + private static final int MAX_HISTORY_LENGTH = 20; + + private final LinkedList myHistory = new LinkedList(); + + private String myVmOptions = ""; + + @Override + public String[] getState() { + synchronized (myHistory) { + String[] res = new String[myHistory.size() + 1]; + res[0] = '#' + myVmOptions; + int i = 1; + for (String s : myHistory) { + res[i++] = s; + } + + return res; + } + } + + @NotNull + public String getVmOptions() { + synchronized (myHistory) { + return myVmOptions; + } + } + + @Override + public void loadState(String[] state) { + synchronized (myHistory) { + myHistory.clear(); + int start = 0; + if (state.length > 0 && state[0].charAt(0) == '#') { + myVmOptions = state[0].substring(1); + start = 1; + } + else { + myVmOptions = ""; + } + + for (int i = start; i < state.length; i++) { + myHistory.add(state[i]); + } + } + } + + public String[] getHistory() { + synchronized (myHistory) { + return ArrayUtil.toStringArray(myHistory); + } + } + + public static MvcRunTargetHistoryService getInstance() { + return ServiceManager.getService(MvcRunTargetHistoryService.class); + } + + public void addCommand(@NotNull String command, @NotNull String vmOptions) { + synchronized (myHistory) { + myVmOptions = vmOptions; + + myHistory.remove(command); + myHistory.addFirst(command); + if (myHistory.size() > MAX_HISTORY_LENGTH) { + myHistory.removeLast(); + } + } + } +}