static void logPlugins() {
    List<String> loadedBundled = new ArrayList<String>();
    List<String> disabled = new ArrayList<String>();
    List<String> loadedCustom = new ArrayList<String>();

    for (IdeaPluginDescriptor descriptor : ourPlugins) {
      final String version = descriptor.getVersion();
      String s = descriptor.getName() + (version != null ? " (" + version + ")" : "");
      if (descriptor.isEnabled()) {
        if (descriptor.isBundled() || SPECIAL_IDEA_PLUGIN.equals(descriptor.getName()))
          loadedBundled.add(s);
        else loadedCustom.add(s);
      } else {
        disabled.add(s);
      }
    }

    Collections.sort(loadedBundled);
    Collections.sort(loadedCustom);
    Collections.sort(disabled);

    getLogger().info("Loaded bundled plugins: " + StringUtil.join(loadedBundled, ", "));
    if (!loadedCustom.isEmpty()) {
      getLogger().info("Loaded custom plugins: " + StringUtil.join(loadedCustom, ", "));
    }
    if (!disabled.isEmpty()) {
      getLogger().info("Disabled plugins: " + StringUtil.join(disabled, ", "));
    }
  }
  public Element getState() {
    final Element element = new Element(COMPONENT_NAME);

    JDOMExternalizerUtil.writeField(
        element, INSTRUMENTATION_TYPE_NAME, myInstrumentationType.toString());
    JDOMExternalizerUtil.writeField(element, LANGUAGE_ANNOTATION_NAME, myLanguageAnnotation);
    JDOMExternalizerUtil.writeField(element, PATTERN_ANNOTATION_NAME, myPatternAnnotation);
    JDOMExternalizerUtil.writeField(element, SUBST_ANNOTATION_NAME, mySubstAnnotation);
    JDOMExternalizerUtil.writeField(
        element, RESOLVE_REFERENCES, String.valueOf(myResolveReferences));

    final List<String> injectorIds = new ArrayList<String>(myInjections.keySet());
    Collections.sort(injectorIds);
    for (String key : injectorIds) {
      final List<BaseInjection> injections = new ArrayList<BaseInjection>(myInjections.get(key));
      if (myDefaultInjections != null) {
        injections.removeAll(myDefaultInjections);
      }
      Collections.sort(
          injections,
          new Comparator<BaseInjection>() {
            public int compare(final BaseInjection o1, final BaseInjection o2) {
              return Comparing.compare(o1.getDisplayName(), o2.getDisplayName());
            }
          });
      for (BaseInjection injection : injections) {
        element.addContent(injection.getState());
      }
    }
    return element;
  }
  public List<HighlightInfo> runMainPasses(
      @NotNull PsiFile psiFile,
      @NotNull Document document,
      @NotNull final ProgressIndicator progress) {
    final List<HighlightInfo> result = new ArrayList<HighlightInfo>();
    final VirtualFile virtualFile = psiFile.getVirtualFile();
    if (virtualFile != null && !virtualFile.getFileType().isBinary()) {

      final List<TextEditorHighlightingPass> passes =
          TextEditorHighlightingPassRegistrarEx.getInstanceEx(myProject)
              .instantiateMainPasses(psiFile, document);

      Collections.sort(
          passes,
          new Comparator<TextEditorHighlightingPass>() {
            @Override
            public int compare(TextEditorHighlightingPass o1, TextEditorHighlightingPass o2) {
              if (o1 instanceof GeneralHighlightingPass) return -1;
              if (o2 instanceof GeneralHighlightingPass) return 1;
              return 0;
            }
          });

      for (TextEditorHighlightingPass pass : passes) {
        pass.doCollectInformation(progress);
        result.addAll(pass.getInfos());
      }
    }

    return result;
  }
  public static String composeText(
      final Map<String, ExpectedHighlightingSet> types,
      Collection<HighlightInfo> infos,
      String text) {
    // filter highlighting data and map each highlighting to a tag name
    List<Pair<String, HighlightInfo>> list =
        ContainerUtil.mapNotNull(
            infos,
            new NullableFunction<HighlightInfo, Pair<String, HighlightInfo>>() {
              @Override
              public Pair<String, HighlightInfo> fun(HighlightInfo info) {
                for (Map.Entry<String, ExpectedHighlightingSet> entry : types.entrySet()) {
                  final ExpectedHighlightingSet set = entry.getValue();
                  if (set.enabled
                      && set.severity == info.getSeverity()
                      && set.endOfLine == info.isAfterEndOfLine()) {
                    return Pair.create(entry.getKey(), info);
                  }
                }
                return null;
              }
            });

    // sort filtered highlighting data by end offset in descending order
    Collections.sort(
        list,
        new Comparator<Pair<String, HighlightInfo>>() {
          @Override
          public int compare(Pair<String, HighlightInfo> o1, Pair<String, HighlightInfo> o2) {
            HighlightInfo i1 = o1.second;
            HighlightInfo i2 = o2.second;

            int byEnds = i2.endOffset - i1.endOffset;
            if (byEnds != 0) return byEnds;

            if (!i1.isAfterEndOfLine() && !i2.isAfterEndOfLine()) {
              int byStarts = i1.startOffset - i2.startOffset;
              if (byStarts != 0) return byStarts;
            } else {
              int byEOL = Comparing.compare(i2.isAfterEndOfLine(), i1.isAfterEndOfLine());
              if (byEOL != 0) return byEOL;
            }

            int bySeverity = i2.getSeverity().compareTo(i1.getSeverity());
            if (bySeverity != 0) return bySeverity;

            return Comparing.compare(i1.getDescription(), i2.getDescription());
          }
        });

    // combine highlighting data with original text
    StringBuilder sb = new StringBuilder();
    Couple<Integer> result = composeText(sb, list, 0, text, text.length(), 0);
    sb.insert(0, text.substring(0, result.second));
    return sb.toString();
  }
 // TODO<rv> Remove the next two methods as a temporary solution. Sort in OrderRootType.
 //
 public static List<OrderRootType> sortRootTypes(Collection<OrderRootType> rootTypes) {
   List<OrderRootType> allTypes = new ArrayList<OrderRootType>(rootTypes);
   Collections.sort(
       allTypes,
       new Comparator<OrderRootType>() {
         @Override
         public int compare(final OrderRootType o1, final OrderRootType o2) {
           return getSortKey(o1).compareTo(getSortKey(o2));
         }
       });
   return allTypes;
 }
 ObjectWithWeight(Object element, String pattern, SpeedSearchComparator comparator) {
   this.node = element;
   final String text = getElementText(element);
   if (text != null) {
     final Iterable<TextRange> ranges = comparator.matchingFragments(pattern, text);
     if (ranges != null) {
       for (TextRange range : ranges) {
         weights.add(range);
       }
     }
   }
   Collections.sort(weights, TEXT_RANGE_COMPARATOR);
 }
  @Override
  public void writeExternal(Element element) throws WriteExternalException {
    final CodeStyleSettings parentSettings = new CodeStyleSettings();
    DefaultJDOMExternalizer.writeExternal(
        this, element, new DifferenceFilter<CodeStyleSettings>(this, parentSettings));
    List<CustomCodeStyleSettings> customSettings =
        new ArrayList<CustomCodeStyleSettings>(getCustomSettingsValues());

    Collections.sort(
        customSettings,
        new Comparator<CustomCodeStyleSettings>() {
          @Override
          public int compare(final CustomCodeStyleSettings o1, final CustomCodeStyleSettings o2) {
            return o1.getTagName().compareTo(o2.getTagName());
          }
        });

    for (final CustomCodeStyleSettings settings : customSettings) {
      final CustomCodeStyleSettings parentCustomSettings =
          parentSettings.getCustomSettings(settings.getClass());
      if (parentCustomSettings == null) {
        throw new WriteExternalException("Custom settings are null for " + settings.getClass());
      }
      settings.writeExternal(element, parentCustomSettings);
    }

    final FileType[] fileTypes =
        myAdditionalIndentOptions
            .keySet()
            .toArray(new FileType[myAdditionalIndentOptions.keySet().size()]);
    Arrays.sort(
        fileTypes,
        new Comparator<FileType>() {
          @Override
          public int compare(final FileType o1, final FileType o2) {
            return o1.getDefaultExtension().compareTo(o2.getDefaultExtension());
          }
        });

    for (FileType fileType : fileTypes) {
      final IndentOptions indentOptions = myAdditionalIndentOptions.get(fileType);
      Element additionalIndentOptions = new Element(ADDITIONAL_INDENT_OPTIONS);
      indentOptions.serialize(additionalIndentOptions, getDefaultIndentOptions(fileType));
      additionalIndentOptions.setAttribute(FILETYPE, fileType.getDefaultExtension());
      if (!additionalIndentOptions.getChildren().isEmpty()) {
        element.addContent(additionalIndentOptions);
      }
    }

    myCommonSettingsManager.writeExternal(element);
  }
 private static void sortInjections(final List<BaseInjection> injections) {
   Collections.sort(
       injections,
       new Comparator<BaseInjection>() {
         public int compare(final BaseInjection o1, final BaseInjection o2) {
           final int support = Comparing.compare(o1.getSupportId(), o2.getSupportId());
           if (support != 0) return support;
           final int lang =
               Comparing.compare(o1.getInjectedLanguageId(), o2.getInjectedLanguageId());
           if (lang != 0) return lang;
           return Comparing.compare(o1.getDisplayName(), o2.getDisplayName());
         }
       });
 }
    @Nullable
    private Object findClosestTo(PsiElement path, ArrayList<ObjectWithWeight> paths) {
      if (path == null || myInitialPsiElement == null) {
        return paths.get(0).node;
      }
      final Set<PsiElement> parents = getAllParents(myInitialPsiElement);
      ArrayList<ObjectWithWeight> cur = new ArrayList<ObjectWithWeight>();
      int max = -1;
      for (ObjectWithWeight p : paths) {
        final Object last = ((TreePath) p.node).getLastPathComponent();
        final List<PsiElement> elements = new ArrayList<PsiElement>();
        final Object object = ((DefaultMutableTreeNode) last).getUserObject();
        if (object instanceof FilteringTreeStructure.FilteringNode) {
          FilteringTreeStructure.FilteringNode node = (FilteringTreeStructure.FilteringNode) object;
          FilteringTreeStructure.FilteringNode candidate = node;

          while (node != null) {
            elements.add(getPsi(node));
            node = node.getParentNode();
          }
          final int size = ContainerUtil.intersection(parents, elements).size();
          if (size == elements.size() - 1
              && size == parents.size() - (myInitialNodeIsLeaf ? 1 : 0)
              && candidate.children().isEmpty()) {
            return p.node;
          }
          if (size > max) {
            max = size;
            cur.clear();
            cur.add(p);
          } else if (size == max) {
            cur.add(p);
          }
        }
      }

      Collections.sort(
          cur,
          new Comparator<ObjectWithWeight>() {
            @Override
            public int compare(ObjectWithWeight o1, ObjectWithWeight o2) {
              final int i = o1.compareWith(o2);
              return i != 0
                  ? i
                  : ((TreePath) o2.node).getPathCount() - ((TreePath) o1.node).getPathCount();
            }
          });
      return cur.isEmpty() ? null : cur.get(0).node;
    }
  public void writeExternal(Element parentNode) throws WriteExternalException {
    Element disableHintsElement = new Element(DISABLE_HINTS_TAG);
    parentNode.addContent(disableHintsElement);

    List<String> array = new ArrayList<String>();
    for (VirtualFile file : myDisabledHintsFiles) {
      if (file.isValid()) {
        array.add(file.getUrl());
      }
    }
    Collections.sort(array);

    for (String url : array) {
      Element fileElement = new Element(FILE_TAG);
      fileElement.setAttribute(URL_ATT, url);
      disableHintsElement.addContent(fileElement);
    }
  }
 protected Element getState(final Element element) {
   Comparator<BaseInjection> comparator =
       new Comparator<BaseInjection>() {
         public int compare(final BaseInjection o1, final BaseInjection o2) {
           return Comparing.compare(o1.getDisplayName(), o2.getDisplayName());
         }
       };
   List<String> injectorIds = new ArrayList<String>(myInjections.keySet());
   Collections.sort(injectorIds);
   for (String key : injectorIds) {
     TreeSet<BaseInjection> injections = new TreeSet<BaseInjection>(comparator);
     injections.addAll(myInjections.get(key));
     injections.removeAll(getDefaultInjections());
     for (BaseInjection injection : injections) {
       element.addContent(injection.getState());
     }
   }
   return element;
 }
  @Override
  public Configurable[] getConfigurables() {
    if (myConfigurables == null) {
      final ArrayList<Configurable> configurables = new ArrayList<Configurable>();
      for (LanguageInjectionSupport support : InjectorUtils.getActiveInjectionSupports()) {
        ContainerUtil.addAll(configurables, support.createSettings(myProject, myConfiguration));
      }
      Collections.sort(
          configurables,
          new Comparator<Configurable>() {
            public int compare(final Configurable o1, final Configurable o2) {
              return Comparing.compare(o1.getDisplayName(), o2.getDisplayName());
            }
          });
      myConfigurables = configurables.toArray(new Configurable[configurables.size()]);
    }

    return myConfigurables;
  }
 public static String toString(Collection<?> collection, String separator) {
   List<String> list =
       ContainerUtil.map2List(
           collection,
           new Function<Object, String>() {
             @Override
             public String fun(final Object o) {
               return String.valueOf(o);
             }
           });
   Collections.sort(list);
   StringBuilder builder = new StringBuilder();
   boolean flag = false;
   for (final String o : list) {
     if (flag) {
       builder.append(separator);
     }
     builder.append(o);
     flag = true;
   }
   return builder.toString();
 }
  private static Graph<PluginId> createPluginIdGraph(
      final Map<PluginId, IdeaPluginDescriptorImpl> idToDescriptorMap) {
    final List<PluginId> ids = new ArrayList<PluginId>(idToDescriptorMap.keySet());
    // this magic ensures that the dependent plugins always follow their dependencies in
    // lexicographic order
    // needed to make sure that extensions are always in the same order
    Collections.sort(
        ids,
        new Comparator<PluginId>() {
          @Override
          public int compare(PluginId o1, PluginId o2) {
            return o2.getIdString().compareTo(o1.getIdString());
          }
        });
    return GraphGenerator.create(
        CachingSemiGraph.create(
            new GraphGenerator.SemiGraph<PluginId>() {
              @Override
              public Collection<PluginId> getNodes() {
                return ids;
              }

              @Override
              public Iterator<PluginId> getIn(PluginId pluginId) {
                final IdeaPluginDescriptor descriptor = idToDescriptorMap.get(pluginId);
                ArrayList<PluginId> plugins = new ArrayList<PluginId>();
                for (PluginId dependentPluginId : descriptor.getDependentPluginIds()) {
                  // check for missing optional dependency
                  if (idToDescriptorMap.containsKey(dependentPluginId)) {
                    plugins.add(dependentPluginId);
                  }
                }
                return plugins.iterator();
              }
            }));
  }
  static void initializePlugins(@Nullable StartupProgress progress) {
    configureExtensions();

    final IdeaPluginDescriptorImpl[] pluginDescriptors = loadDescriptors(progress);

    final Class callerClass = ReflectionUtil.findCallerClass(1);
    assert callerClass != null;
    final ClassLoader parentLoader = callerClass.getClassLoader();

    final List<IdeaPluginDescriptorImpl> result = new ArrayList<IdeaPluginDescriptorImpl>();
    final HashMap<String, String> disabledPluginNames = new HashMap<String, String>();
    for (IdeaPluginDescriptorImpl descriptor : pluginDescriptors) {
      if (descriptor.getPluginId().getIdString().equals(CORE_PLUGIN_ID)) {
        final List<String> modules = descriptor.getModules();
        if (modules != null) {
          ourAvailableModules.addAll(modules);
        }
      }

      if (!shouldSkipPlugin(descriptor, pluginDescriptors)) {
        result.add(descriptor);
      } else {
        descriptor.setEnabled(false);
        disabledPluginNames.put(descriptor.getPluginId().getIdString(), descriptor.getName());
        initClassLoader(parentLoader, descriptor);
      }
    }

    prepareLoadingPluginsErrorMessage(filterBadPlugins(result, disabledPluginNames));

    final Map<PluginId, IdeaPluginDescriptorImpl> idToDescriptorMap =
        new HashMap<PluginId, IdeaPluginDescriptorImpl>();
    for (final IdeaPluginDescriptorImpl descriptor : result) {
      idToDescriptorMap.put(descriptor.getPluginId(), descriptor);
    }

    final IdeaPluginDescriptor corePluginDescriptor =
        idToDescriptorMap.get(PluginId.getId(CORE_PLUGIN_ID));
    assert corePluginDescriptor != null
        : CORE_PLUGIN_ID
            + " not found; platform prefix is "
            + System.getProperty(PlatformUtilsCore.PLATFORM_PREFIX_KEY);
    for (IdeaPluginDescriptorImpl descriptor : result) {
      if (descriptor != corePluginDescriptor) {
        descriptor.insertDependency(corePluginDescriptor);
      }
    }

    mergeOptionalConfigs(idToDescriptorMap);

    // sort descriptors according to plugin dependencies
    Collections.sort(result, getPluginDescriptorComparator(idToDescriptorMap));

    for (int i = 0; i < result.size(); i++) {
      ourId2Index.put(result.get(i).getPluginId(), i);
    }

    int i = 0;
    for (final IdeaPluginDescriptorImpl pluginDescriptor : result) {
      if (pluginDescriptor.getPluginId().getIdString().equals(CORE_PLUGIN_ID)
          || pluginDescriptor.isUseCoreClassLoader()) {
        pluginDescriptor.setLoader(parentLoader, true);
      } else {
        final List<File> classPath = pluginDescriptor.getClassPath();
        final PluginId[] dependentPluginIds = pluginDescriptor.getDependentPluginIds();
        final ClassLoader[] parentLoaders = getParentLoaders(idToDescriptorMap, dependentPluginIds);

        final ClassLoader pluginClassLoader =
            createPluginClassLoader(
                classPath.toArray(new File[classPath.size()]),
                parentLoaders.length > 0 ? parentLoaders : new ClassLoader[] {parentLoader},
                pluginDescriptor);
        pluginDescriptor.setLoader(pluginClassLoader, true);
      }

      pluginDescriptor.registerExtensions();
      if (progress != null) {
        progress.showProgress(
            "", PLUGINS_PROGRESS_MAX_VALUE + (i++ / (float) result.size()) * 0.35f);
      }
    }

    ourPlugins = pluginDescriptors;
  }
  public static String composeText(
      final Map<String, ExpectedHighlightingSet> types,
      Collection<HighlightInfo> infos,
      String text) {
    // filter highlighting data and map each highlighting to a tag name
    List<Pair<String, HighlightInfo>> list =
        ContainerUtil.mapNotNull(
            infos,
            (NullableFunction<HighlightInfo, Pair<String, HighlightInfo>>)
                info -> {
                  for (Map.Entry<String, ExpectedHighlightingSet> entry : types.entrySet()) {
                    final ExpectedHighlightingSet set = entry.getValue();
                    if (set.enabled
                        && set.severity == info.getSeverity()
                        && set.endOfLine == info.isAfterEndOfLine()) {
                      return Pair.create(entry.getKey(), info);
                    }
                  }
                  return null;
                });

    boolean showAttributesKeys = false;
    for (ExpectedHighlightingSet eachSet : types.values()) {
      for (HighlightInfo eachInfo : eachSet.infos) {
        if (eachInfo.forcedTextAttributesKey != null) {
          showAttributesKeys = true;
          break;
        }
      }
    }

    // sort filtered highlighting data by end offset in descending order
    Collections.sort(
        list,
        (o1, o2) -> {
          HighlightInfo i1 = o1.second;
          HighlightInfo i2 = o2.second;

          int byEnds = i2.endOffset - i1.endOffset;
          if (byEnds != 0) return byEnds;

          if (!i1.isAfterEndOfLine() && !i2.isAfterEndOfLine()) {
            int byStarts = i1.startOffset - i2.startOffset;
            if (byStarts != 0) return byStarts;
          } else {
            int byEOL = Comparing.compare(i2.isAfterEndOfLine(), i1.isAfterEndOfLine());
            if (byEOL != 0) return byEOL;
          }

          int bySeverity = i2.getSeverity().compareTo(i1.getSeverity());
          if (bySeverity != 0) return bySeverity;

          return Comparing.compare(i1.getDescription(), i2.getDescription());
        });

    // combine highlighting data with original text
    StringBuilder sb = new StringBuilder();
    Couple<Integer> result = composeText(sb, list, 0, text, text.length(), 0, showAttributesKeys);
    sb.insert(0, text.substring(0, result.second));
    return sb.toString();
  }
  private void createActions(ToolbarDecorator decorator) {
    final Consumer<BaseInjection> consumer =
        new Consumer<BaseInjection>() {
          public void consume(final BaseInjection injection) {
            addInjection(injection);
          }
        };
    final Factory<BaseInjection> producer =
        new NullableFactory<BaseInjection>() {
          public BaseInjection create() {
            final InjInfo info = getSelectedInjection();
            return info == null ? null : info.injection;
          }
        };
    for (LanguageInjectionSupport support : InjectorUtils.getActiveInjectionSupports()) {
      ContainerUtil.addAll(myAddActions, support.createAddActions(myProject, consumer));
      final AnAction action = support.createEditAction(myProject, producer);
      myEditActions.put(
          support.getId(),
          action == null
              ? AbstractLanguageInjectionSupport.createDefaultEditAction(myProject, producer)
              : action);
      mySupports.put(support.getId(), support);
    }
    Collections.sort(
        myAddActions,
        new Comparator<AnAction>() {
          public int compare(final AnAction o1, final AnAction o2) {
            return Comparing.compare(
                o1.getTemplatePresentation().getText(), o2.getTemplatePresentation().getText());
          }
        });
    decorator.disableUpDownActions();
    decorator.setAddActionUpdater(
        new AnActionButtonUpdater() {
          @Override
          public boolean isEnabled(AnActionEvent e) {
            return !myAddActions.isEmpty();
          }
        });
    decorator.setAddAction(
        new AnActionButtonRunnable() {
          @Override
          public void run(AnActionButton button) {
            performAdd(button);
          }
        });
    decorator.setRemoveActionUpdater(
        new AnActionButtonUpdater() {
          @Override
          public boolean isEnabled(AnActionEvent e) {
            boolean enabled = false;
            for (InjInfo info : getSelectedInjections()) {
              if (!info.bundled) {
                enabled = true;
                break;
              }
            }
            return enabled;
          }
        });
    decorator.setRemoveAction(
        new AnActionButtonRunnable() {
          @Override
          public void run(AnActionButton button) {
            performRemove();
          }
        });

    decorator.setEditActionUpdater(
        new AnActionButtonUpdater() {
          @Override
          public boolean isEnabled(AnActionEvent e) {
            AnAction edit = getEditAction();
            if (edit != null) edit.update(e);
            return edit != null && edit.getTemplatePresentation().isEnabled();
          }
        });
    decorator.setEditAction(
        new AnActionButtonRunnable() {
          @Override
          public void run(AnActionButton button) {
            performEditAction();
          }
        });
    decorator.addExtraAction(
        new DumbAwareActionButton("Duplicate", "Duplicate", PlatformIcons.COPY_ICON) {

          @Override
          public boolean isEnabled() {
            return getEditAction() != null;
          }

          @Override
          public void actionPerformed(@NotNull AnActionEvent e) {
            final InjInfo injection = getSelectedInjection();
            if (injection != null) {
              addInjection(injection.injection.copy());
              // performEditAction(e);
            }
          }
        });

    decorator.addExtraAction(
        new DumbAwareActionButton(
            "Enable Selected Injections",
            "Enable Selected Injections",
            PlatformIcons.SELECT_ALL_ICON) {

          @Override
          public void actionPerformed(@NotNull final AnActionEvent e) {
            performSelectedInjectionsEnabled(true);
          }
        });
    decorator.addExtraAction(
        new DumbAwareActionButton(
            "Disable Selected Injections",
            "Disable Selected Injections",
            PlatformIcons.UNSELECT_ALL_ICON) {

          @Override
          public void actionPerformed(@NotNull final AnActionEvent e) {
            performSelectedInjectionsEnabled(false);
          }
        });

    new DumbAwareAction("Toggle") {
      @Override
      public void update(@NotNull AnActionEvent e) {
        SpeedSearchSupply supply = SpeedSearchSupply.getSupply(myInjectionsTable);
        e.getPresentation().setEnabled(supply == null || !supply.isPopupActive());
      }

      @Override
      public void actionPerformed(@NotNull final AnActionEvent e) {
        performToggleAction();
      }
    }.registerCustomShortcutSet(
        new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0)), myInjectionsTable);

    if (myInfos.length > 1) {
      AnActionButton shareAction =
          new DumbAwareActionButton("Move to IDE Scope", null, PlatformIcons.IMPORT_ICON) {
            {
              addCustomUpdater(
                  new AnActionButtonUpdater() {
                    @Override
                    public boolean isEnabled(AnActionEvent e) {
                      CfgInfo cfg = getTargetCfgInfo(getSelectedInjections());
                      e.getPresentation()
                          .setText(
                              cfg == getDefaultCfgInfo()
                                  ? "Move to IDE Scope"
                                  : "Move to Project Scope");
                      return cfg != null;
                    }
                  });
            }

            @Override
            public void actionPerformed(@NotNull final AnActionEvent e) {
              final List<InjInfo> injections = getSelectedInjections();
              final CfgInfo cfg = getTargetCfgInfo(injections);
              if (cfg == null) return;
              for (InjInfo info : injections) {
                if (info.cfgInfo == cfg) continue;
                if (info.bundled) continue;
                info.cfgInfo.injectionInfos.remove(info);
                cfg.addInjection(info.injection);
              }
              final int[] selectedRows = myInjectionsTable.getSelectedRows();
              myInjectionsTable.getListTableModel().setItems(getInjInfoList(myInfos));
              TableUtil.selectRows(myInjectionsTable, selectedRows);
            }

            @Nullable
            private CfgInfo getTargetCfgInfo(final List<InjInfo> injections) {
              CfgInfo cfg = null;
              for (InjInfo info : injections) {
                if (info.bundled) {
                  continue;
                }
                if (cfg == null) cfg = info.cfgInfo;
                else if (cfg != info.cfgInfo) return info.cfgInfo;
              }
              if (cfg == null) return null;
              for (CfgInfo info : myInfos) {
                if (info != cfg) return info;
              }
              throw new AssertionError();
            }
          };
      shareAction.setShortcut(
          new CustomShortcutSet(
              KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.SHIFT_DOWN_MASK)));
      decorator.addExtraAction(shareAction);
    }
    decorator.addExtraAction(
        new DumbAwareActionButton("Import", "Import", AllIcons.Actions.Install) {

          @Override
          public void actionPerformed(@NotNull final AnActionEvent e) {
            doImportAction(e.getDataContext());
            updateCountLabel();
          }
        });
    decorator.addExtraAction(
        new DumbAwareActionButton("Export", "Export", AllIcons.Actions.Export) {

          @Override
          public void actionPerformed(@NotNull final AnActionEvent e) {
            final List<BaseInjection> injections = getInjectionList(getSelectedInjections());
            final VirtualFileWrapper wrapper =
                FileChooserFactory.getInstance()
                    .createSaveFileDialog(
                        new FileSaverDescriptor("Export Selected Injections to File...", "", "xml"),
                        myProject)
                    .save(null, null);
            if (wrapper == null) return;
            final Configuration configuration = new Configuration();
            configuration.setInjections(injections);
            final Document document = new Document(configuration.getState());
            try {
              JDOMUtil.writeDocument(document, wrapper.getFile(), "\n");
            } catch (IOException ex) {
              final String msg = ex.getLocalizedMessage();
              Messages.showErrorDialog(
                  myProject,
                  msg != null && msg.length() > 0 ? msg : ex.toString(),
                  "Export Failed");
            }
          }

          @Override
          public boolean isEnabled() {
            return !getSelectedInjections().isEmpty();
          }
        });
  }