private static JBPopup getPsiElementPopup(
      final Object[] elements,
      final Map<PsiElement, GotoRelatedItem> itemsMap,
      final String title,
      final Processor<Object> processor) {

    final Ref<Boolean> hasMnemonic = Ref.create(false);
    final DefaultPsiElementCellRenderer renderer =
        new DefaultPsiElementCellRenderer() {
          {
            setFocusBorderEnabled(false);
          }

          @Override
          public String getElementText(PsiElement element) {
            String customName = itemsMap.get(element).getCustomName();
            return (customName != null ? customName : super.getElementText(element));
          }

          @Override
          protected Icon getIcon(PsiElement element) {
            Icon customIcon = itemsMap.get(element).getCustomIcon();
            return customIcon != null ? customIcon : super.getIcon(element);
          }

          @Override
          public String getContainerText(PsiElement element, String name) {
            PsiFile file = element.getContainingFile();
            return file != null && !getElementText(element).equals(file.getName())
                ? "(" + file.getName() + ")"
                : null;
          }

          @Override
          protected DefaultListCellRenderer getRightCellRenderer() {
            return null;
          }

          @Override
          protected boolean customizeNonPsiElementLeftRenderer(
              ColoredListCellRenderer renderer,
              JList list,
              Object value,
              int index,
              boolean selected,
              boolean hasFocus) {
            final GotoRelatedItem item = (GotoRelatedItem) value;
            Color color = list.getForeground();
            final SimpleTextAttributes nameAttributes = new SimpleTextAttributes(Font.PLAIN, color);
            final String name = item.getCustomName();
            if (name == null) return false;
            renderer.append(name, nameAttributes);
            renderer.setIcon(item.getCustomIcon());
            return true;
          }

          @Override
          public Component getListCellRendererComponent(
              JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
            final JPanel component =
                (JPanel)
                    super.getListCellRendererComponent(
                        list, value, index, isSelected, cellHasFocus);
            if (!hasMnemonic.get()) return component;

            final JPanel panelWithMnemonic = new JPanel(new BorderLayout());
            final int mnemonic = getMnemonic(value, itemsMap);
            final JLabel label = new JLabel("");
            if (mnemonic != -1) {
              label.setText(mnemonic + ".");
              label.setDisplayedMnemonicIndex(0);
            }
            label.setPreferredSize(new JLabel("8.").getPreferredSize());

            final JComponent leftRenderer = (JComponent) component.getComponents()[0];
            component.remove(leftRenderer);
            panelWithMnemonic.setBackground(leftRenderer.getBackground());
            label.setBackground(leftRenderer.getBackground());
            panelWithMnemonic.add(label, BorderLayout.WEST);
            panelWithMnemonic.add(leftRenderer, BorderLayout.CENTER);
            component.add(panelWithMnemonic);
            return component;
          }
        };
    final ListPopupImpl popup =
        new ListPopupImpl(
            new BaseListPopupStep<Object>(title, Arrays.asList(elements)) {
              @Override
              public boolean isSpeedSearchEnabled() {
                return true;
              }

              @Override
              public PopupStep onChosen(Object selectedValue, boolean finalChoice) {
                processor.process(selectedValue);
                return super.onChosen(selectedValue, finalChoice);
              }
            }) {
          @Override
          protected ListCellRenderer getListElementRenderer() {
            return renderer;
          }
        };
    popup.setMinimumSize(new Dimension(200, -1));
    for (Object item : elements) {
      final int mnemonic = getMnemonic(item, itemsMap);
      if (mnemonic != -1) {
        final Action action = createNumberAction(mnemonic, popup, itemsMap, processor);
        popup.registerAction(
            mnemonic + "Action", KeyStroke.getKeyStroke(String.valueOf(mnemonic)), action);
        hasMnemonic.set(true);
      }
    }
    return popup;
  }
  private static JBPopup getPsiElementPopup(
      final Object[] elements,
      final Map<PsiElement, GotoRelatedItem> itemsMap,
      final String title,
      final boolean showContainingModules,
      final Processor<Object> processor) {

    final Ref<Boolean> hasMnemonic = Ref.create(false);
    final DefaultPsiElementCellRenderer renderer =
        new DefaultPsiElementCellRenderer() {
          {
            setFocusBorderEnabled(false);
          }

          @Override
          public String getElementText(PsiElement element) {
            String customName = itemsMap.get(element).getCustomName();
            return (customName != null ? customName : super.getElementText(element));
          }

          @Override
          protected Icon getIcon(PsiElement element) {
            Icon customIcon = itemsMap.get(element).getCustomIcon();
            return customIcon != null ? customIcon : super.getIcon(element);
          }

          @Override
          public String getContainerText(PsiElement element, String name) {
            String customContainerName = itemsMap.get(element).getCustomContainerName();

            if (customContainerName != null) {
              return customContainerName;
            }
            PsiFile file = element.getContainingFile();
            return file != null && !getElementText(element).equals(file.getName())
                ? "(" + file.getName() + ")"
                : null;
          }

          @Override
          protected DefaultListCellRenderer getRightCellRenderer(Object value) {
            return showContainingModules ? super.getRightCellRenderer(value) : null;
          }

          @Override
          protected boolean customizeNonPsiElementLeftRenderer(
              ColoredListCellRenderer renderer,
              JList list,
              Object value,
              int index,
              boolean selected,
              boolean hasFocus) {
            final GotoRelatedItem item = (GotoRelatedItem) value;
            Color color = list.getForeground();
            final SimpleTextAttributes nameAttributes =
                new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, color);
            final String name = item.getCustomName();
            if (name == null) return false;
            renderer.append(name, nameAttributes);
            renderer.setIcon(item.getCustomIcon());
            return true;
          }

          @Override
          public Component getListCellRendererComponent(
              JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
            final JPanel component =
                (JPanel)
                    super.getListCellRendererComponent(
                        list, value, index, isSelected, cellHasFocus);
            if (!hasMnemonic.get()) return component;

            final JPanel panelWithMnemonic = new JPanel(new BorderLayout());
            final int mnemonic = getMnemonic(value, itemsMap);
            final JLabel label = new JLabel("");
            if (mnemonic != -1) {
              label.setText(mnemonic + ".");
              label.setDisplayedMnemonicIndex(0);
            }
            label.setPreferredSize(new JLabel("8.").getPreferredSize());

            final JComponent leftRenderer = (JComponent) component.getComponents()[0];
            component.remove(leftRenderer);
            panelWithMnemonic.setBorder(BorderFactory.createEmptyBorder(0, 7, 0, 0));
            panelWithMnemonic.setBackground(leftRenderer.getBackground());
            label.setBackground(leftRenderer.getBackground());
            panelWithMnemonic.add(label, BorderLayout.WEST);
            panelWithMnemonic.add(leftRenderer, BorderLayout.CENTER);
            component.add(panelWithMnemonic);
            return component;
          }
        };
    final ListPopupImpl popup =
        new ListPopupImpl(
            new BaseListPopupStep<Object>(title, Arrays.asList(elements)) {
              @Override
              public boolean isSpeedSearchEnabled() {
                return true;
              }

              @Override
              public String getIndexedString(Object value) {
                if (value instanceof GotoRelatedItem) {
                  //noinspection ConstantConditions
                  return ((GotoRelatedItem) value).getCustomName();
                }
                PsiElement element = (PsiElement) value;
                if (!element.isValid()) return "INVALID";
                return renderer.getElementText(element)
                    + " "
                    + renderer.getContainerText(element, null);
              }

              @Override
              public PopupStep onChosen(Object selectedValue, boolean finalChoice) {
                processor.process(selectedValue);
                return super.onChosen(selectedValue, finalChoice);
              }
            }) {};
    popup
        .getList()
        .setCellRenderer(
            new PopupListElementRenderer(popup) {
              Map<Object, String> separators = new HashMap<Object, String>();

              {
                final ListModel model = popup.getList().getModel();
                String current = null;
                boolean hasTitle = false;
                for (int i = 0; i < model.getSize(); i++) {
                  final Object element = model.getElementAt(i);
                  final GotoRelatedItem item = itemsMap.get(element);
                  if (item != null && !StringUtil.equals(current, item.getGroup())) {
                    current = item.getGroup();
                    separators.put(element, current);
                    if (!hasTitle && !StringUtil.isEmpty(current)) {
                      hasTitle = true;
                    }
                  }
                }

                if (!hasTitle) {
                  separators.remove(model.getElementAt(0));
                }
              }

              @Override
              public Component getListCellRendererComponent(
                  JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
                final Component component =
                    renderer.getListCellRendererComponent(
                        list, value, index, isSelected, cellHasFocus);
                final String separator = separators.get(value);

                if (separator != null) {
                  JPanel panel = new JPanel(new BorderLayout());
                  panel.add(component, BorderLayout.CENTER);
                  final SeparatorWithText sep =
                      new SeparatorWithText() {
                        @Override
                        protected void paintComponent(Graphics g) {
                          g.setColor(new JBColor(Color.WHITE, UIUtil.getSeparatorColor()));
                          g.fillRect(0, 0, getWidth(), getHeight());
                          super.paintComponent(g);
                        }
                      };
                  sep.setCaption(separator);
                  panel.add(sep, BorderLayout.NORTH);
                  return panel;
                }
                return component;
              }
            });

    popup.setMinimumSize(new Dimension(200, -1));

    for (Object item : elements) {
      final int mnemonic = getMnemonic(item, itemsMap);
      if (mnemonic != -1) {
        final Action action = createNumberAction(mnemonic, popup, itemsMap, processor);
        popup.registerAction(
            mnemonic + "Action", KeyStroke.getKeyStroke(String.valueOf(mnemonic)), action);
        popup.registerAction(
            mnemonic + "Action",
            KeyStroke.getKeyStroke("NUMPAD" + String.valueOf(mnemonic)),
            action);
        hasMnemonic.set(true);
      }
    }
    return popup;
  }