1. Introduction
1.介绍
Over the past few years, IntelliJ from JetBrains has quickly become the top IDE for Java developers. In our most recent State of Java report, IntelliJ was the IDE of choice for 55% of respondents, up from 48% the year before.
在过去几年中,来自 JetBrains 的 IntelliJ 已迅速成为 Java 开发人员的顶级 IDE。在我们最近的Java 状况报告中,IntelliJ 是 55% 受访者的首选 IDE,高于前一年的 48%。
One feature that makes IntelliJ so appealing to Java developers is the ability to extend and create new functionality using plugins. In this tutorial, we’ll look at writing an IntelliJ plugin to demonstrate a few of the ways of extending the IDE.
使得 IntelliJ 对 Java 开发者如此有吸引力的一个特点是能够使用插件来扩展和创建新功能。在本教程中,我们将研究如何编写一个IntelliJ插件,以演示扩展IDE的几种方法。
And note that while this article is focused on IntelliJ plugins, all of the JetBrains IDEs share common code. Therefore, many of the techniques used here can be applied to other JetBrain’s IDEs such as PyCharm, RubyMine, and more.
并注意到,虽然本文主要是针对IntelliJ插件,但所有的JetBrains IDE都有共同的代码。因此,这里使用的许多技术都可以应用于JetBrain的其他IDE,如PyCharm、RubyMine等。
2. Plugin Functionality
2.插件的功能
Plugin functionality for IntelliJ typically falls into one of 4 categories:
IntelliJ的插件功能通常分为4类之一。
- Custom languages: the ability to write, interpret, and compile code written in different languages
- Frameworks: support for 3rd party frameworks such as Spring
- Tools: integration with external tools such as Gradle
- User interface add-ons: new menu items, tool windows and buttons, and more
Plugins will often fall into multiple categories. For example, the Git plugin that ships with IntelliJ, interacts with the git executable installed on the system. The plugin provides its tool window and popup menu items, while also integrating into the project creation workflow, preferences window, and more.
插件通常属于多个类别。例如,随 IntelliJ 提供的 Git 插件,可与系统中安装的 git 可执行程序进行交互。该插件提供其工具窗口和弹出式菜单项目,同时还集成到项目创建工作流程、偏好窗口等。
3. Creating a Plugin
3.创建一个插件
The easiest way to get started with IntelliJ plugins is using their Plugin DevKit. This can be accessed from the New > Project menu:
开始使用 IntelliJ 插件的最简单方法是使用其插件开发工具包。这可以从新建>项目菜单中访问。
Note we must use a JetBrains JDK to ensure the required plugin classes are available on the classpath. IntelliJ should come with a suitable JDK by default, but if not we can download one from here.
注意我们必须使用JetBrains JDK以确保所需的插件类在classpath上可用。IntelliJ 默认情况下应该带有合适的 JDK,但如果没有,我们可以从这里下载一个。
As of this writing, we can only use Java 8 for writing IntelliJ plugins. This is because JetBrains does not currently provide an official JDK for Java 9 or higher.
截至目前,我们只能使用Java 8来编写IntelliJ插件。这是因为JetBrains目前没有为Java 9或更高版本提供官方的JDK。
4. Example Plugin
4.插件实例
To demonstrate writing an IntelliJ plugin, we’ll create a plugin that provides quick access to the popular Stack Overflow website from multiple areas in the IDE. We’ll add:
为了演示编写IntelliJ插件,我们将创建一个插件,从IDE的多个区域快速访问流行的Stack Overflow网站。我们将添加。
- A Tools menu item to visit the Ask a Question page
- A popup menu item in both text editor and console output to search Stack Overflow for highlighted text.
4.1. Creating Actions
4.1.创建行动
Actions are the core component used for writing IntelliJ plugins. Actions get triggered by events in the IDE, such as clicking a menu item or toolbar button.
动作是用于编写IntelliJ插件的核心组件。行动是由IDE中的事件触发的,如点击菜单项或工具栏按钮。
The first step in creating an action is to create a Java class that extends AnAction. For our Stack Overflow plugin, we’ll create 2 actions.
创建一个动作的第一步是创建一个扩展AnAction的Java类。对于我们的Stack Overflow插件,我们将创建2个动作。
The first action opens the Ask a Question page in a new browser window:
第一个动作是在一个新的浏览器窗口中打开 “提问 “页面。
public class AskQuestionAction extends AnAction {
@Override
public void actionPerformed(AnActionEvent e) {
BrowserUtil.browse("https://stackoverflow.com/questions/ask");
}
}
We use the built-in BrowserUtil class because it handles all the nuances of opening a web page on different operating systems and browsers.
我们使用内置的BrowserUtil类,因为它能处理在不同操作系统和浏览器上打开网页的所有细微差别。
The second action opens the Stack Overflow search page and passes search text as a query string. This time we’ll implement two methods.
第二个动作是打开Stack Overflow的搜索页面,并将搜索文本作为查询字符串传递。这一次我们将实现两个方法。
The first method we implement is just like our first action and handles opening a web browser.
我们实现的第一个方法就像我们的第一个动作一样,处理打开一个网络浏览器。
First, though, we need to collect two values for StackOverflow. One is the language tag, and the other is the text to search for.
不过,首先,我们需要为StackOverflow收集两个值。一个是语言标签,另一个是要搜索的文本。
To get the language tag, we’ll use the Program Structure Interface. This API parses all the files in a project and provides a programmatic way to inspect them.
为了获得语言标签,我们将使用程序结构接口。该API解析了项目中的所有文件,并提供了一种检查这些文件的编程方式。
In this case, we use the PSI to determine the programming language of a file:
在这种情况下,我们使用PSI来确定一个文件的编程语言。
PsiFile file = e.getData(CommonDataKeys.PSI_FILE);
Language lang = e.getData(CommonDataKeys.PSI_FILE).getLanguage();
String languageTag = "+[" + lang.getDisplayName().toLowerCase() + "]";
Note that the PSI also provides language-specific details about a file. For example, we could use the PSI to find all public methods in a Java class.
请注意,PSI还提供了关于一个文件的特定语言细节。例如,我们可以使用PSI来查找一个Java类中的所有公共方法。
To get the text to search for, we’ll use the Editor API to retrieve highlighted text on the screen:
为了获得要搜索的文本,我们将使用Editor API来检索屏幕上的高亮文本。
final Editor editor = e.getRequiredData(CommonDataKeys.EDITOR);
CaretModel caretModel = editor.getCaretModel();
String selectedText = caretModel.getCurrentCaret().getSelectedText();
Even though this action is the same for both editor and console windows, accessing the selected text works the same way.
尽管这个动作对编辑器和控制台窗口都是一样的,访问所选文本的方式也是一样的。
Now, we can put this all together in an actionPerformed declaration:
现在,我们可以把这一切放在一个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);
}
This action also overrides a second method named update. This allows us to enable or disable the action under different conditions.
这个动作还覆盖了第二个名为update的方法。这允许我们在不同条件下启用或禁用该动作。
In this case, we disable the search action when there no selected text:
在这种情况下,当没有选中的文本时,我们就禁用搜索动作。
@Override
public void update(AnActionEvent e) {
Editor editor = e.getRequiredData(CommonDataKeys.EDITOR);
CaretModel caretModel = editor.getCaretModel();
e.getPresentation().setEnabledAndVisible(caretModel.getCurrentCaret().hasSelection());
}
4.2. Registering Actions
4.2.注册行动
Once we have our actions written, we need to register them with the IDE. There are two ways to do this.
一旦我们写好了动作,我们需要在IDE中注册它们。有两种方法可以做到这一点。
The first way is using the plugin.xml file, which is created for us when we start a new project.
第一种方法是使用plugin.xml文件,当我们开始一个新项目时,它会为我们创建。
By default the file will have an empty <actions> element, which is where we will add our actions:
默认情况下,该文件将有一个空的<actions>元素,这就是我们要添加动作的地方。
<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>
Using the XML file to register actions will ensure they register during IDE startup, which is usually preferable.
使用XML文件来注册动作将确保它们在IDE启动时注册,这通常是最好的。
The second way to register actions is programmatically using the ActionManager class:
注册动作的第二种方式是通过编程使用ActionManager类。
ActionManager.getInstance().registerAction("StackOverflow.SearchAction", new SearchAction());
This has the advantage of letting us dynamically register actions. For example, if we write a plugin to integrate with a remote API, we might want to register a different set of actions based on the version of the API that we call.
这样做的好处是让我们动态地注册动作。例如,如果我们写一个插件来集成一个远程API,我们可能想根据我们调用的API的版本来注册一组不同的动作。
The disadvantage to this approach is that actions do not register at startup. We have to create an instance of ApplicationComponent to manage actions, which requires more coding and XML configuration.
这种方法的缺点是,动作在启动时不会注册。我们必须创建一个ApplicationComponent的实例来管理动作,这需要更多的编码和XML配置。
5. Testing the Plugin
5.测试该插件
As with any program, writing an IntelliJ plugin requires testing. For a small plugin like the one we have written, it’s sufficient to ensure the plugin compiles and that the actions we created work as expected when we click them.
与任何程序一样,编写IntelliJ插件需要测试。对于像我们写的这个小插件来说,只要确保插件的编译和我们创建的动作在我们点击它们时按预期工作就足够了。
We can manually test (and debug) our plugin by using a Plugin run configuration:
我们可以通过使用插件运行配置来手动测试(和调试)我们的插件。
This will launch a new instance of IntelliJ with our plugin activated. This allows us to click the different menu items we created and ensure the proper Stack Overflow pages open up.
这将启动一个新的IntelliJ实例,并激活我们的插件。这使我们能够点击我们创建的不同的菜单项,并确保打开适当的Stack Overflow页面。
If you wish to do more traditional unit testing, IntelliJ provides a headless environment to run unit tests. We can write tests using any test framework we want, and the tests run using real, unmocked components from the IDE.
如果您希望进行更传统的单元测试,IntelliJ提供了一个无头环境来运行单元测试。我们可以使用任何我们想要的测试框架来编写测试,而测试则使用IDE中真实的、未被模拟的组件运行。
6. Deploying the Plugin
6.部署插件
The plugin DevKit provides a simple way to package plugins so we can install and distribute them. Simply right-click the plugin project and select “Prepare plugin module for Deployment”. This will generate a JAR file inside the project directory.
插件DevKit提供了一个简单的方法来打包插件,以便我们可以安装和分发它们。只需右击插件项目并选择 “为部署准备插件模块”。这将在项目目录内生成一个JAR文件。
The generated JAR file contains the code and configuration files needed to load into IntelliJ. You can install it locally, or publish it to a plugin repository for use by others.
生成的 JAR 文件包含加载到 IntelliJ 所需的代码和配置文件。您可以在本地安装它,或者将它发布到插件库,供他人使用。
The screenshot below shows one of the new Stack Overflow menu items in action:
下面的截图显示了新的Stack Overflow菜单项目之一的运作情况。
7. Conclusion
7.结论
In this article, we developed a simple plugin that highlights just a few of how we can enhance the IntelliJ IDE.
在这篇文章中,我们开发了一个简单的插件,它只是强调了我们如何增强IntelliJ IDE的几个方面。
While we primarily worked with actions, the IntelliJ plugin SDK offers several ways to add new functionality to the IDE. For further reading, check out the official getting started guide.
虽然我们主要使用动作,但 IntelliJ 插件 SDK 提供了几种向 IDE 添加新功能的方法。如需进一步阅读,请查看官方的入门指南。
As always, the full code for our sample plugin can be found in our GitHub repository.
一如既往,我们的示例插件的完整代码可以在我们的GitHub 仓库中找到。