diff --git a/plugins/gradle/gradle.iml b/plugins/gradle/gradle.iml
index 925c1ab193ac..4dbad25039f1 100644
--- a/plugins/gradle/gradle.iml
+++ b/plugins/gradle/gradle.iml
@@ -6,6 +6,7 @@
+
@@ -176,6 +177,7 @@
+
diff --git a/plugins/gradle/resources/i18n/GradleBundle.properties b/plugins/gradle/resources/i18n/GradleBundle.properties
index 67e121dfebea..26ed2bc1f194 100644
--- a/plugins/gradle/resources/i18n/GradleBundle.properties
+++ b/plugins/gradle/resources/i18n/GradleBundle.properties
@@ -22,4 +22,8 @@ gradle.action.open.script.description=Allows to open project file of the linked
gradle.notifications.use.distribution.with.sources=You can configure Gradle wrapper to use distribution with sources. \
It will provide IDE with Gradle API/DSL documentation.
gradle.notifications.hide.tip=Hide the tip
-gradle.notifications.apply.suggestion=Ok, apply suggestion!
\ No newline at end of file
+gradle.notifications.apply.suggestion=Ok, apply suggestion!
+
+gradle.codeInsight.action.apply_plugin.text=Add Gradle plugin
+gradle.codeInsight.action.apply_plugin.description=Add selected gradle plugin to the project
+gradle.codeInsight.action.apply_plugin.popup.title=Choose plugin
\ No newline at end of file
diff --git a/plugins/gradle/resources/i18n/GradleDocumentationBundle.properties b/plugins/gradle/resources/i18n/GradleDocumentationBundle.properties
index 857b741c2b60..1c185408c4dc 100644
--- a/plugins/gradle/resources/i18n/GradleDocumentationBundle.properties
+++ b/plugins/gradle/resources/i18n/GradleDocumentationBundle.properties
@@ -42,13 +42,13 @@ gradle.documentation.org.gradle.api.Project.apply.plugin.announce=\
gradle.documentation.org.gradle.api.Project.apply.plugin.build-announcements=\
Sends local announcements to your desktop about interesting events in the build lifecycle.
gradle.documentation.org.gradle.api.Project.apply.plugin.checkstyle=\
- Performs quality checks on your project's Java source files using\
+
Performs quality checks on your project's Java source files using \
Checkstyle \
and generates reports from these checks.\
gradle.documentation.org.gradle.api.Project.apply.plugin.codenarc=\
Performs quality checks on your project's Groovy source files using \
- CodeNarc \
+ CodeNarc \
and generates reports from these checks.\
gradle.documentation.org.gradle.api.Project.apply.plugin.eclipse=\
@@ -62,7 +62,7 @@ gradle.documentation.org.gradle.api.Project.apply.plugin.eclipse-wtp=\
gradle.documentation.org.gradle.api.Project.apply.plugin.findbugs=\
Performs quality checks on your project's Java source files using \
- FindBugs \
+ FindBugs \
and generates reports from these checks.\
gradle.documentation.org.gradle.api.Project.apply.plugin.idea=\
@@ -76,7 +76,7 @@ gradle.documentation.org.gradle.api.Project.apply.plugin.jdepend=\
gradle.documentation.org.gradle.api.Project.apply.plugin.pmd=\
Performs quality checks on your project's Java source files using \
- PMD \
+ PMD \
and generates reports from these checks.\
gradle.documentation.org.gradle.api.Project.apply.plugin.project-report=\
diff --git a/plugins/gradle/resources/icons/gradlePlugin.png b/plugins/gradle/resources/icons/gradlePlugin.png
new file mode 100644
index 000000000000..a342f298cc99
Binary files /dev/null and b/plugins/gradle/resources/icons/gradlePlugin.png differ
diff --git a/plugins/gradle/src/META-INF/plugin.xml b/plugins/gradle/src/META-INF/plugin.xml
index fd2e97e78fa4..9cb03439b612 100644
--- a/plugins/gradle/src/META-INF/plugin.xml
+++ b/plugins/gradle/src/META-INF/plugin.xml
@@ -93,5 +93,10 @@
-
+
+
+
+
+
+
diff --git a/plugins/gradle/src/icons/GradleIcons.java b/plugins/gradle/src/icons/GradleIcons.java
index e12bafb0d0c2..4283f7207364 100644
--- a/plugins/gradle/src/icons/GradleIcons.java
+++ b/plugins/gradle/src/icons/GradleIcons.java
@@ -31,6 +31,7 @@ public class GradleIcons {
public static final Icon Gradle = load("/icons/gradle.png"); // 16x16
public static final Icon GradleImport = load("/icons/gradleImport.png"); // 16x16
public static final Icon GradleNavigate = load("/icons/gradleNavigate.png"); // 16x16
+ public static final Icon GradlePlugin = load("/icons/gradlePlugin.png"); // 16x16
public static final Icon GradleSync = load("/icons/gradleSync.png"); // 16x16
public static final Icon ToolWindowGradle = load("/icons/toolWindowGradle.png"); // 13x13
}
diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/codeInsight/actions/AddGradleDslPluginAction.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/codeInsight/actions/AddGradleDslPluginAction.java
new file mode 100644
index 000000000000..fa9bcf7a9064
--- /dev/null
+++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/codeInsight/actions/AddGradleDslPluginAction.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.plugins.gradle.codeInsight.actions;
+
+import com.intellij.codeInsight.CodeInsightActionHandler;
+import com.intellij.codeInsight.actions.CodeInsightAction;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.KeyValue;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.PsiCompiledElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.util.Function;
+import com.intellij.util.containers.ContainerUtil;
+import icons.GradleIcons;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.plugins.gradle.util.GradleBundle;
+import org.jetbrains.plugins.gradle.util.GradleConstants;
+import org.jetbrains.plugins.gradle.util.GradleDocumentationBundle;
+import org.jetbrains.plugins.groovy.GroovyFileType;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/22/13
+ */
+public class AddGradleDslPluginAction extends CodeInsightAction {
+ public static final ThreadLocal TEST_THREAD_LOCAL = new ThreadLocal();
+ private final KeyValue[] myPlugins;
+
+ public AddGradleDslPluginAction() {
+ getTemplatePresentation().setDescription(GradleBundle.message("gradle.codeInsight.action.apply_plugin.description"));
+ getTemplatePresentation().setText(GradleBundle.message("gradle.codeInsight.action.apply_plugin.text"));
+ getTemplatePresentation().setIcon(GradleIcons.GradlePlugin);
+
+ final List plugins = StringUtil.split(
+ "java,groovy,idea,eclipse,scala,antlr,application,ear,jetty,maven,osgi,war,announce," +
+ "build-announcements,checkstyle,codenarc,eclipse-wtp,findbugs,jdepend,pmd,project-report,signing,sonar", ",");
+
+ myPlugins = new KeyValue[plugins.size()];
+ ContainerUtil.map2Array(plugins, myPlugins, new Function() {
+ @Override
+ public KeyValue fun(String o) {
+ return createPluginKey(o);
+ }
+ });
+ Arrays.sort(myPlugins, new Comparator() {
+ @Override
+ public int compare(KeyValue o1, KeyValue o2) {
+ return String.valueOf(o1.getKey()).compareTo(String.valueOf(o2.getKey()));
+ }
+ });
+ }
+
+ @NotNull
+ @Override
+ protected CodeInsightActionHandler getHandler() {
+ return new AddGradleDslPluginActionHandler(myPlugins);
+ }
+
+ @Override
+ protected boolean isValidForFile(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
+ if (file instanceof PsiCompiledElement) return false;
+ if (!GroovyFileType.GROOVY_FILE_TYPE.equals(file.getFileType())) return false;
+ return !GradleConstants.SETTINGS_FILE_NAME.equals(file.getName()) && file.getName().endsWith(GradleConstants.EXTENSION);
+ }
+
+ @NotNull
+ private static KeyValue createPluginKey(@NotNull String pluginName) {
+ String description =
+ GradleDocumentationBundle.messageOrDefault("gradle.documentation.org.gradle.api.Project.apply.plugin." + pluginName, "");
+ return KeyValue.create(pluginName, description);
+ }
+}
diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/codeInsight/actions/AddGradleDslPluginActionHandler.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/codeInsight/actions/AddGradleDslPluginActionHandler.java
new file mode 100644
index 000000000000..540fdc9a878e
--- /dev/null
+++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/codeInsight/actions/AddGradleDslPluginActionHandler.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.plugins.gradle.codeInsight.actions;
+
+import com.intellij.codeInsight.CodeInsightActionHandler;
+import com.intellij.codeInsight.CodeInsightUtilBase;
+import com.intellij.codeInsight.lookup.impl.LookupCellRenderer;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.command.WriteCommandAction;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.colors.EditorColorsManager;
+import com.intellij.openapi.editor.colors.EditorColorsScheme;
+import com.intellij.openapi.editor.colors.EditorFontType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.KeyValue;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.ui.components.JBList;
+import com.intellij.util.Function;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.plugins.gradle.util.GradleBundle;
+import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
+import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
+import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
+
+import javax.swing.*;
+import java.awt.*;
+
+/**
+* @author Vladislav.Soroka
+* @since 10/24/13
+*/
+class AddGradleDslPluginActionHandler implements CodeInsightActionHandler {
+ private final KeyValue[] myPlugins;
+ public AddGradleDslPluginActionHandler(KeyValue[] plugins) {
+ myPlugins = plugins;
+ }
+
+ @Override
+ public void invoke(@NotNull final Project project, @NotNull final Editor editor, @NotNull final PsiFile file) {
+ if (!CodeInsightUtilBase.prepareEditorForWrite(editor)) return;
+
+ final JBList list = new JBList(myPlugins);
+ list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ list.setCellRenderer(new MyListCellRenderer());
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ final KeyValue selected = (KeyValue)list.getSelectedValue();
+ new WriteCommandAction.Simple(project, GradleBundle.message("gradle.codeInsight.action.apply_plugin.text"), file) {
+ @Override
+ protected void run() {
+ if (selected == null) return;
+ GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(project);
+ GrStatement grStatement = factory.createStatementFromText(
+ String.format("apply plugin: '%s'", selected.getKey()), null);
+
+ PsiElement anchor = file.findElementAt(editor.getCaretModel().getOffset());
+ PsiElement currentElement = PsiTreeUtil.getParentOfType(anchor, GrClosableBlock.class, GroovyFile.class);
+ if (currentElement != null) {
+ currentElement.addAfter(grStatement, anchor);
+ }
+ else {
+ file.addAfter(grStatement, file.findElementAt(editor.getCaretModel().getOffset() - 1));
+ }
+ PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
+ Document document = documentManager.getDocument(file);
+ if (document != null) {
+ documentManager.commitDocument(document);
+ }
+ }
+ }.execute();
+ }
+ };
+
+ if (ApplicationManager.getApplication().isUnitTestMode()) {
+ KeyValue descriptor = ContainerUtil.find(myPlugins, new Condition() {
+ @Override
+ public boolean value(KeyValue value) {
+ return value.getKey().equals(AddGradleDslPluginAction.TEST_THREAD_LOCAL.get());
+ }
+ });
+ list.setSelectedValue(descriptor, false);
+ runnable.run();
+ }
+ else {
+ JBPopupFactory.getInstance().createListPopupBuilder(list)
+ .setTitle(GradleBundle.message("gradle.codeInsight.action.apply_plugin.popup.title"))
+ .setItemChoosenCallback(runnable)
+ .setFilteringEnabled(new Function() {
+ @Override
+ public String fun(Object o) {
+ return String.valueOf(((KeyValue)o).getKey());
+ }
+ })
+ .createPopup()
+ .showInBestPositionFor(editor);
+ }
+ }
+
+ @Override
+ public boolean startInWriteAction() {
+ return false;
+ }
+
+ private static class MyListCellRenderer implements ListCellRenderer {
+ private final JPanel myPanel;
+ private final JLabel myNameLabel;
+ private final JLabel myDescLabel;
+
+ public MyListCellRenderer() {
+ myPanel = new JPanel(new BorderLayout());
+ myPanel.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 0));
+ myNameLabel = new JLabel();
+
+ myPanel.add(myNameLabel, BorderLayout.WEST);
+ myPanel.add(new JLabel(" "));
+ myDescLabel = new JLabel();
+ myPanel.add(myDescLabel, BorderLayout.EAST);
+
+ EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
+ Font font = scheme.getFont(EditorFontType.PLAIN);
+ myNameLabel.setFont(font);
+ myDescLabel.setFont(font);
+ }
+
+ @Override
+ public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
+
+ KeyValue descriptor = (KeyValue)value;
+ Color backgroundColor = isSelected ? list.getSelectionBackground() : list.getBackground();
+
+ myNameLabel.setText(String.valueOf(descriptor.getKey()));
+ myNameLabel.setForeground(isSelected ? list.getSelectionForeground() : list.getForeground());
+ myPanel.setBackground(backgroundColor);
+
+ String description = String.format("%s
", 400, String.valueOf(descriptor.getValue()));
+ myDescLabel.setText(description);
+ myDescLabel.setForeground(LookupCellRenderer.getGrayedForeground(isSelected));
+ myDescLabel.setBackground(backgroundColor);
+
+ return myPanel;
+ }
+ }
+}
diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/codeInsight/actions/GradleGenerateActionGroup.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/codeInsight/actions/GradleGenerateActionGroup.java
new file mode 100644
index 000000000000..18530edb7f99
--- /dev/null
+++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/codeInsight/actions/GradleGenerateActionGroup.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.plugins.gradle.codeInsight.actions;
+
+import com.intellij.openapi.actionSystem.DefaultActionGroup;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/22/13
+ */
+public class GradleGenerateActionGroup extends DefaultActionGroup {
+ public GradleGenerateActionGroup() {
+ add(new AddGradleDslPluginAction());
+ }
+}
\ No newline at end of file
diff --git a/plugins/gradle/testData/testApplyPlugin.gradle b/plugins/gradle/testData/testApplyPlugin.gradle
new file mode 100644
index 000000000000..f90814d636ed
--- /dev/null
+++ b/plugins/gradle/testData/testApplyPlugin.gradle
@@ -0,0 +1 @@
+//empty file
\ No newline at end of file
diff --git a/plugins/gradle/testData/testApplyPluginInsideClosure.gradle b/plugins/gradle/testData/testApplyPluginInsideClosure.gradle
new file mode 100644
index 000000000000..6e96bac9c4a8
--- /dev/null
+++ b/plugins/gradle/testData/testApplyPluginInsideClosure.gradle
@@ -0,0 +1,6 @@
+
+apply plugin: "groovy"
+
+allprojects {
+ //
+}
\ No newline at end of file
diff --git a/plugins/gradle/testData/testApplyPluginInsideClosure_after.gradle b/plugins/gradle/testData/testApplyPluginInsideClosure_after.gradle
new file mode 100644
index 000000000000..023847fe0b42
--- /dev/null
+++ b/plugins/gradle/testData/testApplyPluginInsideClosure_after.gradle
@@ -0,0 +1,6 @@
+
+apply plugin: "groovy"
+
+allprojects {
+ //
+apply plugin: 'java'}
\ No newline at end of file
diff --git a/plugins/gradle/testData/testApplyPlugin_after.gradle b/plugins/gradle/testData/testApplyPlugin_after.gradle
new file mode 100644
index 000000000000..1dd49648e03c
--- /dev/null
+++ b/plugins/gradle/testData/testApplyPlugin_after.gradle
@@ -0,0 +1,2 @@
+//empty file
+apply plugin: 'java'
\ No newline at end of file
diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/codeInsight/actions/AddGradleDslPluginActionTest.java b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/codeInsight/actions/AddGradleDslPluginActionTest.java
new file mode 100644
index 000000000000..6e0e9edd07f2
--- /dev/null
+++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/codeInsight/actions/AddGradleDslPluginActionTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2000-2013 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.plugins.gradle.codeInsight.actions;
+
+import com.intellij.testFramework.IdeaTestCase;
+import com.intellij.testFramework.PlatformTestUtil;
+import com.intellij.testFramework.fixtures.CodeInsightTestUtil;
+import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase;
+
+import java.io.File;
+
+/**
+ * @author Vladislav.Soroka
+ * @since 10/23/13
+ */
+public class AddGradleDslPluginActionTest extends LightPlatformCodeInsightFixtureTestCase {
+
+ public AddGradleDslPluginActionTest() {
+ IdeaTestCase.initPlatformPrefix();
+ }
+
+ public void testApplyPlugin() throws Exception {
+ doTest("testApplyPlugin.gradle", "java");
+ }
+
+ public void testApplyPluginInsideClosure() throws Exception {
+ myFixture.configureByFile("testApplyPluginInsideClosure.gradle");
+ doTest("testApplyPluginInsideClosure.gradle", "java");
+ }
+
+ private void doTest(String file, String pluginName) {
+ AddGradleDslPluginAction.TEST_THREAD_LOCAL.set(pluginName);
+ CodeInsightTestUtil.doActionTest(new AddGradleDslPluginAction(), file, myFixture);
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected String getBasePath() {
+ return "/plugins/gradle/testData";
+ }
+
+ @Override
+ protected String getTestDataPath() {
+ return PlatformTestUtil.getCommunityPath().replace(File.separatorChar, '/') + getBasePath();
+ }
+}