diff --git a/.idea/modules.xml b/.idea/modules.xml
index 971465f76091..8bdc9c9c84ca 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -10,6 +10,7 @@
+
diff --git a/plugins/ShortcutPromoter/ShortcutPromoter.iml b/plugins/ShortcutPromoter/ShortcutPromoter.iml
new file mode 100644
index 000000000000..83561d9e1025
--- /dev/null
+++ b/plugins/ShortcutPromoter/ShortcutPromoter.iml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/ShortcutPromoter/resources/META-INF/plugin.xml b/plugins/ShortcutPromoter/resources/META-INF/plugin.xml
new file mode 100644
index 000000000000..a9a1188b3c77
--- /dev/null
+++ b/plugins/ShortcutPromoter/resources/META-INF/plugin.xml
@@ -0,0 +1,21 @@
+
+ Shortcut Promoter
+ com.intellij.promoter
+ 1.0
+ JetBrains
+ The plugin advertises to use shortcuts instead of mouse clicks
+ com.intellij.modules.platform
+
+
+
+ com.intellij.promoter.ShortcutPromoterManager
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/ShortcutPromoter/src/com/intellij/promoter/PromoterState.java b/plugins/ShortcutPromoter/src/com/intellij/promoter/PromoterState.java
new file mode 100644
index 000000000000..b44b378b4dc4
--- /dev/null
+++ b/plugins/ShortcutPromoter/src/com/intellij/promoter/PromoterState.java
@@ -0,0 +1,20 @@
+package com.intellij.promoter;
+
+/**
+ * @author Konstantin Bulenkov
+ */
+public class PromoterState {
+ private int myClicks;
+
+ public int getClicks() {
+ return myClicks;
+ }
+
+ public void setClicks(int clicks) {
+ myClicks = clicks;
+ }
+
+ public void incClicks() {
+ myClicks++;
+ }
+}
diff --git a/plugins/ShortcutPromoter/src/com/intellij/promoter/ShortcutPromoterEP.java b/plugins/ShortcutPromoter/src/com/intellij/promoter/ShortcutPromoterEP.java
new file mode 100644
index 000000000000..7fe76262dac6
--- /dev/null
+++ b/plugins/ShortcutPromoter/src/com/intellij/promoter/ShortcutPromoterEP.java
@@ -0,0 +1,32 @@
+package com.intellij.promoter;
+
+import com.intellij.openapi.extensions.AbstractExtensionPointBean;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.util.xmlb.annotations.Attribute;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Konstantin Bulenkov
+ */
+public class ShortcutPromoterEP extends AbstractExtensionPointBean {
+ public static final ExtensionPointName EP_NAME = new ExtensionPointName("com.intellij.shortcutPromoter");
+
+ @Attribute("actionId")
+ public String actionId;
+
+ @Attribute("skip")
+ public int skip;
+
+ @Attribute("repeat")
+ public int repeat;
+
+ @Nullable
+ public static ShortcutPromoterEP find(@Nullable String actionId) {
+ for (ShortcutPromoterEP ep : EP_NAME.getExtensions()) {
+ if (ep.actionId.equals(actionId)) {
+ return ep;
+ }
+ }
+ return null;
+ }
+}
diff --git a/plugins/ShortcutPromoter/src/com/intellij/promoter/ShortcutPromoterManager.java b/plugins/ShortcutPromoter/src/com/intellij/promoter/ShortcutPromoterManager.java
new file mode 100644
index 000000000000..02356825404b
--- /dev/null
+++ b/plugins/ShortcutPromoter/src/com/intellij/promoter/ShortcutPromoterManager.java
@@ -0,0 +1,122 @@
+package com.intellij.promoter;
+
+import com.intellij.openapi.actionSystem.ActionManager;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.actionSystem.ex.AnActionListener;
+import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.components.*;
+import org.jdom.Element;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.awt.event.InputEvent;
+import java.awt.event.MouseEvent;
+import java.io.File;
+import java.util.HashMap;
+
+/**
+ * @author Konstantin Bulenkov
+ */
+@State(
+ name = "ShortcutPromoterManager",
+ roamingType = RoamingType.PER_PLATFORM,
+ storages = {
+ @Storage(
+ file = StoragePathMacros.APP_CONFIG + "/promoter.xml"
+ )}
+)
+public class ShortcutPromoterManager implements ApplicationComponent, AnActionListener, ExportableApplicationComponent,
+ PersistentStateComponent {
+ private final HashMap myState = new HashMap();
+ private final HashMap myExtensions = new HashMap();
+
+ @Override
+ public void initComponent() {
+ myExtensions.clear();
+ myState.clear();
+
+ for (ShortcutPromoterEP ep : ShortcutPromoterEP.EP_NAME.getExtensions()) {
+ myExtensions.put(ep.actionId, ep);
+ }
+ ActionManager.getInstance().addAnActionListener(this);
+ }
+
+ @Override
+ public void disposeComponent() {
+ ActionManager.getInstance().removeAnActionListener(this);
+ }
+
+ @NotNull
+ @Override
+ public String getComponentName() {
+ return getClass().getName();
+ }
+
+ @Override
+ public void beforeActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) {
+ final InputEvent input = event.getInputEvent();
+ if (input instanceof MouseEvent) {
+ final String id = ActionManager.getInstance().getId(action);
+ final ShortcutPromoterEP ep = myExtensions.get(id);
+ if (ep != null) {
+ PromoterState state = myState.get(id);
+ if (state == null) {
+ state = new PromoterState();
+ myState.put(id, state);
+ }
+ state.incClicks();
+ }
+ }
+ }
+
+ @Override
+ public void afterActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) {
+
+ }
+
+ @Override
+ public void beforeEditorTyping(char c, DataContext dataContext) {
+
+ }
+
+ @NotNull
+ @Override
+ public File[] getExportFiles() {
+ return new File[]{PathManager.getOptionsFile("shortcutPromoter")};
+ }
+
+ @NotNull
+ @Override
+ public String getPresentableName() {
+ return "Shortcut Promoter";
+ }
+
+ @Nullable
+ @Override
+ public Element getState() {
+ final Element actions = new Element("actions");
+ for (String id : myState.keySet()) {
+ final Element action = new Element("action");
+ action.setAttribute("actionId", id);
+ action.setAttribute("clicks", String.valueOf(myState.get(id).getClicks()));
+ actions.addContent(action);
+ }
+ return actions;
+ }
+
+ @Override
+ public void loadState(Element state) {
+ myState.clear();
+ for (Element action : state.getChildren("action")) {
+ final String id = action.getAttributeValue("actionId");
+ final String clicks = action.getAttributeValue("clicks");
+ try {
+ final PromoterState info = new PromoterState();
+ info.setClicks(Integer.parseInt(clicks));
+ myState.put(id, info);
+ } catch (Exception ignore) {}
+ }
+ }
+}
diff --git a/python/main_pycharm_ce.iml b/python/main_pycharm_ce.iml
index 67303ad79cbc..8a72b6d348fd 100644
--- a/python/main_pycharm_ce.iml
+++ b/python/main_pycharm_ce.iml
@@ -18,6 +18,7 @@
+