«1. Введение

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

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

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

2. Основные типы подключаемых модулей

Наиболее распространенные типы подключаемых модулей включают следующие функции:

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

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

3. Создание плагина

Существует два поддерживаемых способа создания плагинов. Мы будем использовать рекомендуемый способ для новых проектов с Gradle вместо использования их Plugin Devkit.

Создание плагина на основе Gradle выполняется с помощью меню «Создать» \u003e «Проект».

Обратите внимание, что мы должны включить Java и подключаемый модуль платформы IntelliJ, чтобы обеспечить доступность необходимых классов подключаемых модулей в пути к классам.

На момент написания этой статьи мы можем использовать JDK 8 только для написания плагинов IntelliJ.

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

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

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

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

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

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

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

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

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

Для выполнения поиска в StackOverflow нам нужны два параметра: тег языка и текст для поиска.

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

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

Optional<PsiFile> psiFile = Optional.ofNullable(e.getData(LangDataKeys.PSI_FILE));
String languageTag = psiFile.map(PsiFile::getLanguage)
  .map(Language::getDisplayName)
  .map(String::toLowerCase)
  .map(lang -> "[" + lang + "]")
  .orElse("");

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

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

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

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

@Override
public void actionPerformed(@NotNull AnActionEvent e) {
    Optional<PsiFile> psiFile = Optional.ofNullable(e.getData(LangDataKeys.PSI_FILE));
    String languageTag = psiFile.map(PsiFile::getLanguage)
      .map(Language::getDisplayName)
      .map(String::toLowerCase)
      .map(lang -> "[" + lang + "]")
      .orElse("");

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

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

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

Editor editor = e.getRequiredData(CommonDataKeys.EDITOR);
CaretModel caretModel = editor.getCaretModel();
e.getPresentation().setEnabledAndVisible(caretModel.getCurrentCaret().hasSelection());

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

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

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

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

<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>

«

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

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

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

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

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

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

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

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

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

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

6. Развертывание плагина

Плагин Gradle предоставляет простой способ упаковки плагинов, чтобы мы могли их устанавливать и распространять. Просто откройте окно инструмента Gradle и выполните задачу buildPlugin. Это создаст ZIP-файл внутри каталога build/distributions.

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

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

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

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

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