«1. Введение

За последние несколько лет IntelliJ от JetBrains быстро стала лучшей IDE для Java-разработчиков. В нашем последнем отчете о состоянии Java 55% респондентов выбрали IntelliJ по сравнению с 48% годом ранее.

Одной из особенностей, которая делает IntelliJ столь привлекательным для разработчиков Java, является возможность расширять и создавать новые функции с помощью подключаемых модулей. В этом руководстве мы рассмотрим написание подключаемого модуля IntelliJ, чтобы продемонстрировать несколько способов расширения IDE.

И обратите внимание, что хотя эта статья посвящена подключаемым модулям IntelliJ, все IDE JetBrains имеют общий код. Таким образом, многие из используемых здесь методов могут быть применены к другим IDE JetBrain, таким как PyCharm, RubyMine и другим.

2. Функциональность подключаемого модуля

Функциональность подключаемого модуля для IntelliJ обычно относится к одной из 4 категорий:

    Пользовательские языки: возможность писать, интерпретировать и компилировать код, написанный на разных языках. Платформы: поддержка сторонних сред, таких как как Spring Tools: интеграция с внешними инструментами, такими как надстройки пользовательского интерфейса Gradle: новые пункты меню, окна инструментов и кнопки и многое другое

Плагины часто попадают в несколько категорий. Например, подключаемый модуль Git, поставляемый с IntelliJ, взаимодействует с исполняемым файлом git, установленным в системе. Плагин предоставляет свое окно инструментов и элементы всплывающего меню, а также интегрируется в рабочий процесс создания проекта, окно настроек и многое другое.

3. Создание подключаемого модуля

Самый простой способ начать работу с подключаемыми модулями IntelliJ — использовать их Plugin DevKit. Доступ к этому можно получить из меню «Создать» \u003e «Проект»:

Обратите внимание, что мы должны использовать JetBrains JDK, чтобы обеспечить доступность необходимых классов подключаемых модулей в пути к классам. IntelliJ должен поставляться с подходящим JDK по умолчанию, но если нет, мы можем загрузить его отсюда.

На момент написания этой статьи мы можем использовать только Java 8 для написания плагинов IntelliJ. Это связано с тем, что JetBrains в настоящее время не предоставляет официальный JDK для Java 9 или выше.

4. Пример подключаемого модуля

Чтобы продемонстрировать написание подключаемого модуля IntelliJ, мы создадим подключаемый модуль, обеспечивающий быстрый доступ к популярному веб-сайту Stack Overflow из нескольких областей IDE. Мы добавим:

    Пункт меню «Инструменты» для перехода на страницу «Задать вопрос» Пункт всплывающего меню как в текстовом редакторе, так и в выводе консоли для поиска выделенного текста в переполнении стека.

4.1. Создание действий

Действия — это основной компонент, используемый для написания подключаемых модулей IntelliJ. Действия запускаются событиями в среде IDE, такими как нажатие элемента меню или кнопки на панели инструментов.

Первым шагом в создании действия является создание класса Java, расширяющего AnAction. Для нашего плагина Stack Overflow мы создадим 2 действия.

Первое действие открывает страницу Задать вопрос в новом окне браузера:

public class AskQuestionAction extends AnAction {
   @Override
   public void actionPerformed(AnActionEvent e) {
       BrowserUtil.browse("https://stackoverflow.com/questions/ask");
   }
}

Мы используем встроенный класс BrowserUtil, потому что он обрабатывает все нюансы открытия веб-страницы в разных операционных системах и браузерах.

Второе действие открывает страницу поиска Stack Overflow и передает текст поиска в виде строки запроса. На этот раз мы реализуем два метода.

Первый метод, который мы реализуем, аналогичен нашему первому действию и обрабатывает открытие веб-браузера.

Но сначала нам нужно собрать два значения для StackOverflow. Один — это языковой тег, а другой — текст для поиска.

Чтобы получить тег языка, мы будем использовать интерфейс структуры программы. Этот API анализирует все файлы в проекте и предоставляет программный способ их проверки.

В этом случае мы используем PSI для определения языка программирования файла:

PsiFile file = e.getData(CommonDataKeys.PSI_FILE);
Language lang = e.getData(CommonDataKeys.PSI_FILE).getLanguage();
String languageTag = "+[" + lang.getDisplayName().toLowerCase() + "]";

Обратите внимание, что PSI также предоставляет сведения о файле, зависящие от языка. Например, мы могли бы использовать PSI для поиска всех общедоступных методов в классе Java.

Чтобы получить текст для поиска, мы будем использовать API редактора для извлечения выделенного текста на экране:

final Editor editor = e.getRequiredData(CommonDataKeys.EDITOR);
CaretModel caretModel = editor.getCaretModel();
String selectedText = caretModel.getCurrentCaret().getSelectedText();

Несмотря на то, что это действие одинаково для окон редактора и консоли, доступ к выделенному тексту работает так же.

Теперь мы можем объединить все это в объявлении actionPerformed:

@Override
public void actionPerformed(AnActionEvent e) {

    PsiFile file = e.getData(CommonDataKeys.PSI_FILE);
    Language lang = e.getData(CommonDataKeys.PSI_FILE).getLanguage();
    String languageTag = "+[" + lang.getDisplayName().toLowerCase() + "]";

    Editor editor = e.getRequiredData(CommonDataKeys.EDITOR);
    CaretModel caretModel = editor.getCaretModel();
    String selectedText = caretModel.getCurrentCaret().getSelectedText()

    String query = selectedText.replace(' ', '+') + languageTag;
    BrowserUtil.browse("https://stackoverflow.com/search?q=" + query);
}

«

«Это действие также переопределяет второй метод с именем update. Это позволяет нам включать или отключать действие при различных условиях.

@Override
public void update(AnActionEvent e) {
     Editor editor = e.getRequiredData(CommonDataKeys.EDITOR);
     CaretModel caretModel = editor.getCaretModel();
     e.getPresentation().setEnabledAndVisible(caretModel.getCurrentCaret().hasSelection());
}

В этом случае отключаем действие поиска при отсутствии выделенного текста:

4.2. Регистрация действий

После того, как мы написали действия, нам нужно зарегистрировать их в среде IDE. Есть два способа сделать это.

Первый способ — использовать файл plugin.xml, который создается для нас, когда мы начинаем новый проект.

<actions>
    <action 
      id="StackOverflow.AskQuestion.ToolsMenu"
      class="com.baeldung.intellij.stackoverflowplugin.AskQuestionAction"
      text="Ask Question on Stack Overflow"
      description="Ask a Question on Stack Overflow">
        <add-to-group group-id="ToolsMenu" anchor="last"/>
    </action>
    <action 
      id="StackOverflow.Search.Editor"
      class="com.baeldung.intellij.stackoverflowplugin.SearchAction"
      text="Search on Stack Overflow"
      description="Search on Stack Overflow">
        <add-to-group group-id="EditorPopupMenu" anchor="last"/>
    </action>
    <action 
      id="StackOverflow.Search.Console"
      class="com.baeldung.intellij.stackoverflowplugin.SearchAction"
      text="Search on Stack Overflow"
      description="Search on Stack Overflow">
        <add-to-group group-id="ConsoleEditorPopupMenu" anchor="last"/>
    </action>
</actions>

По умолчанию в файле будет пустой элемент \u003cactions\u003e, куда мы добавим наши действия:

Использование XML-файла для регистрации действий обеспечит их регистрацию во время запуска IDE, что обычно предпочтительнее .

ActionManager.getInstance().registerAction("StackOverflow.SearchAction", new SearchAction());

Второй способ зарегистрировать действия — программно использовать класс ActionManager:

Преимущество этого заключается в том, что мы можем динамически регистрировать действия. Например, если мы пишем подключаемый модуль для интеграции с удаленным API, нам может потребоваться зарегистрировать другой набор действий в зависимости от версии API, который мы вызываем.

Недостатком этого подхода является то, что действия не регистрируются при запуске. Нам нужно создать экземпляр ApplicationComponent для управления действиями, что требует дополнительного кодирования и настройки XML.

5. Тестирование подключаемого модуля

Как и в случае с любой программой, написание подключаемого модуля IntelliJ требует тестирования. Для небольшого плагина, такого как тот, который мы написали, достаточно убедиться, что плагин компилируется и что созданные нами действия работают должным образом, когда мы нажимаем на них.

Мы можем вручную протестировать (и отладить) наш плагин, используя конфигурацию запуска плагина:

Это запустит новый экземпляр IntelliJ с активированным нашим плагином. Это позволяет нам щелкать различные элементы меню, которые мы создали, и обеспечивать открытие соответствующих страниц переполнения стека.

Если вы хотите выполнить более традиционное модульное тестирование, IntelliJ предоставляет безголовую среду для запуска модульных тестов. Мы можем писать тесты, используя любую тестовую среду, которую захотим, и тесты запускаются с использованием реальных немокаемых компонентов из IDE.

6. Развертывание подключаемого модуля

Комплект DevKit для подключаемых модулей предоставляет простой способ упаковки подключаемых модулей, чтобы мы могли их устанавливать и распространять. Просто щелкните проект плагина правой кнопкой мыши и выберите «Подготовить модуль плагина к развертыванию». Это создаст файл JAR внутри каталога проекта.

Сгенерированный файл JAR содержит код и файлы конфигурации, необходимые для загрузки в IntelliJ. Вы можете установить его локально или опубликовать в репозитории плагинов для использования другими.

На приведенном ниже снимке экрана показан один из новых пунктов меню Stack Overflow в действии:

7. Заключение

В этой статье мы разработали простой подключаемый модуль, который показывает лишь некоторые способы улучшения IntelliJ IDE.

Хотя в основном мы работали с действиями, SDK подключаемого модуля IntelliJ предлагает несколько способов добавления новых функций в IDE. Для дальнейшего чтения ознакомьтесь с официальным руководством по началу работы.