第三十九章. IDEA 插件

Chapter 39. The IDEA Plugin

IDEA 插件生成 IntelliJ IDEA 使用的文件,从而可以让项目从 IDEA 中打开(File - Open Project)。它同时考虑到了外部依赖(包括关联的源代码和 javadoc 文件)及项目依赖。
The IDEA plugin generates files that are used by IntelliJ IDEA, thus making it possible to open the project from IDEA (File - Open Project). Both external dependencies (including associated source and javadoc files) and project dependencies are considered.

IDEA 插件生成的内容具体取决于使用了哪些其他插件:
What exactly the IDEA plugin generates depends on which other plugins are used:

表 39.1. IDEA 插件行为 - Table 39.1. IDEA plugin behavior

插件
Plugin
描述
Description
None 生成一个 IDEA 模块文件。如果该项目是根项目,还会生成一个 IDEA 项目和工作区文件。
Generates an IDEA module file. Also generates an IDEA project and workspace file if the project is the root project.
Java 将 Java 配置添加到模块和项目文件中。
Adds Java configuration to the module and project files.

IDEA 插件的一个重点是开放定制。该插件提供了一组标准化的钩子,用于添加和删除生成的文件中的内容。
One focus of the IDEA plugin is to be open to customization. The plugin provides a standardized set of hooks for adding and removing content from the generated files.

39.1. 用法

39.1. Usage

要使用 IDEA 插件,请在构建脚本中包含以下语句:
To use the IDEA plugin, include this in your build script:

示例 39.1. 使用 IDEA 插件 - Example 39.1. Using the IDEA plugin

build.gradle

apply plugin: 'idea'

IDEA 插件向的项目添加了许多任务。你将使用的主要任务是 ideacleanIdea
The IDEA plugin adds a number of tasks to your project. The main tasks that you will use are the idea and cleanIdea tasks.

39.2. 任务

39.2. Tasks

IDEA 插件向一个项目添加如下所示的任务。请注意,clean 并不依赖于 cleanIdeaWorkspace。这是因为工作空间包含许多用户特定的临时数据,并且通常不希望在 IDEA 之外操作它。
The IDEA plugin adds the tasks shown below to a project. Notice that clean does not depend on cleanIdeaWorkspace. It's because workspace contains a lot of user specific temporary data and typically it is not desirable to manipulate it outside IDEA.

表 39.2. IDEA 插件——任务 - Table 39.2. IDEA plugin - Tasks

任务名称
Task name
依赖于
Depends on
类型
Type
描述
Description
idea ideaProject, ideaModule, ideaWorkspace - 生成所有的 IDEA 配置文件
Generates all IDEA configuration files
cleanIdea cleanIdeaProject, cleanIdeaModule Delete 删除所有的 IDEA 配置文件
Removes all IDEA configuration files
cleanIdeaProject - Delete 删除 IDEA 项目文件
Removes the IDEA project file
cleanIdeaModule - Delete 删除 IDEA 模块文件
Removes the IDEA module file
cleanIdeaWorkspace - Delete 删除 IDEA 工作区文件
Removes the IDEA workspace file
ideaProject - GenerateIdeaProject 生成 .ipr 文件。此任务仅添加到根项目。
Generates the .ipr file. This task is only added to the root project.
ideaModule - GenerateIdeaModule 生成 .iml 文件
Generates the .iml file
ideaWorkspace - GenerateIdeaWorkspace 生成 .iws 文件。此任务仅添加到根项目。
Generates the .iws file. This task is only added to the root project.

39.3. 配置

39.3. Configuration

表 39.3. idea 插件的配置 - Table 39.3. Configuration of the idea plugin

模型
Model
引用名称
Reference name
描述
Description
IdeaModel idea 顶级元素,支持以 DSL 友好的方式配置 idea 插件
Top level element that enables configuration of the idea plugin in a DSL-friendly fashion
IdeaProject idea.project 允许配置项目信息
Allows configuring project information
IdeaModule idea.module 允许配置模块信息
Allows configuring module information
IdeaWorkspace idea.workspace 允许配置工作区 XML
Allows configuring the workspace XML

39.4. 自定义生成的文件

39.4. Customizing the generated files

IDEA插件提供了一些钩子和行为,用于自定义生成的内容。工作区文件可以有效地只通过withXml钩子来操作,因为它对应的域对象本质上是空的。
IDEA plugin provides hooks and behavior for customizing the generated content. The workspace file can effectively only be manipulated via the withXml hook because its corresponding domain object is essentially empty.

该任务会识别现有的IDEA文件,并将它们与生成的内容进行合并。
The tasks recognize existing IDEA files, and merge them with the generated content.

39.4.1. 合并

39.4.1. Merging

已存在的IDEA文件的部分,也是生成的目标内容,将会被修改或覆盖,具体取决于特定的部分。其余部分将保持原样。
Sections of existing IDEA files that are also the target of generated content will be amended or overwritten, depending on the particular section. The remaining sections will be left as-is.

39.4.1.1. 使用完全覆盖禁用合并

39.4.1.1. Disabling merging with a complete overwrite

要完全覆盖现有IDEA文件,请执行clean任务及其相应的生成任务,例如gradle cleanIdea idea(按此顺序)。如果要让它成为默认行为,请添加 tasks.idea.dependsOn(cleanIdea) 到构建脚本中。这样我们就不用显式执行清理任务了。
To completely overwrite existing IDEA files, execute a clean task together with its corresponding generation task, for example gradle cleanIdea idea (in that order). If you want to make this the default behavior, add tasks.idea.dependsOn(cleanIdea) to your build script. This makes it unnecessary to execute the clean task explicitly.

完全覆盖同样可以用于个人文件,例如通过执行gradle cleanIdeaModule ideaModule
Complete overwrite works equally well for individual files, for example by executing gradle cleanIdeaModule ideaModule.

39.4.2. Hooking 到生成的生命周期中

39.4.2. Hooking into the generation lifecycle

该插件提供了一些对象,对由Gradle生成的元数据文件部分进行建模。生成的生命周期如下:
The plugin provides objects modeling the sections of the metadata files that are generated by Gradle. The generation lifecycle is as follows:

  1. 读取文件;如果它不存在,则使用由 Gradle 提供的默认版本
  2. beforeMerged的钩子会使用一个表示现有文件的域对象执行
  3. 从 Gradle 构建推断出来或在 eclipse DSL 中显示定义的配置会与现有的内容合并在一起
  4. whenMerged的钩子会使用一个表示持久化的域对象执行
  5. withXml的钩子会使用一个表示将被持久化的XML的 raw 执行
  6. 最终的 XML 被持久化

下表列出了用于每种模型类型的域对象:
The following table lists the domain object used for each of the model types:

表 39.4. Idea 插件钩子 - Table 39.4. Idea plugin hooks

模型
Model
beforeMerged { arg -> } 参数类型
beforeMerged { arg -> } argument type
whenMerged { arg -> } 参数类型
whenMerged { arg -> } argument type
withXml { arg -> } 参数类型
withXml { arg -> } argument type
IdeaProject Project Project XmlProvider
IdeaModule Module Module XmlProvider
IdeaWorkspace Workspace Workspace XmlProvider

39.4.2.1. 部分覆盖现有内容

39.4.2.1. Partial overwrite of existing content

完全覆盖会导致现有的所有内容被丢弃,从而丢失直接在 IDE 中进行的任何更改。beforeMerged 钩子使得可以只覆盖现有内容的某些部分。以下示例为从 Module 域对象中删除现有的所有依赖:
A complete overwrite causes all existing content to be discarded, thereby losing any changes made directly in the IDE. The beforeMerged hook makes it possible to overwrite just certain parts of the existing content. The following example removes all existing dependencies from the Module domain object:

示例 39.2. 部分覆盖模块 - Example 39.2. Partial Overwrite for Module

build.gradle

idea.module.iml {
    beforeMerged { module ->
        module.dependencies.clear()
    }
}


生成的模块文件将仅包含Gradle生成的依赖项,但不包含原始文件中可能存在的任何其他依赖项。(对于依赖项,这也是默认行为。)模块文件的其他部分将保持原样或合并。对项目文件中的模块路径也可以这样做:

The resulting module file will only contain Gradle-generated dependency entries, but not any other dependency entries that may have been present in the original file. (In the case of dependency entries, this is also the default behavior.) Other sections of the module file will be either left as-is or merged. The same could be done for the module paths in the project file:

示例 39.3. 项目的部分覆盖 - Example 39.3. Partial Overwrite for Project

build.gradle

idea.project.ipr {
    beforeMerged { project ->
        project.modulePaths.clear()
    }
}


39.4.2.2. 修改完全填充的域对象

39.4.2.2. Modifying the fully populated domain objects

whenMerged 钩子允许操作完全填充的域对象。通常,这是自定义IDEA文件的首选方法。下面的例子展示了如何导出一个IDEA模块的所有依赖:
The whenMerged hook allows to manipulate the fully populated domain objects. Often this is the preferred way to customize IDEA files. Here is how you would export all the dependencies of an IDEA module:

示例 39.4. 导出依赖 - Example 39.4. Export Dependencies

build.gradle

idea.module.iml {
    whenMerged { module ->
        module.dependencies*.exported = true
    }
}


39.4.2.3. 修改 XML 的表示形式

39.4.2.3. Modifying the XML representation

withXml钩子允许在文件写入磁盘之前操作内存中的 XML 表示形式。尽管 Groovy 的 XML 支持对其帮助很大,但这种方法比起操作域对象依然很不方便。作为回报,你可以对生成的文件进行完全的控制,包括未由域对象建模的部分。
The withXmlhook allows to manipulate the in-memory XML representation just before the file gets written to disk. Although Groovy's XML support makes up for a lot, this approach is less convenient than manipulating the domain objects. In return, you get total control over the generated file, including sections not modeled by the domain objects.

示例 39.5. 自定义 XML - Example 39.5. Customizing the XML

build.gradle

idea.project.ipr {
    withXml { provider ->
        provider.node.component.find { it.@name == 'VcsDirectoryMappings' }.mapping.@vcs = 'Git'
    }
}


39.5. 还需要考虑的事情

39.5. Further things to consider

生成的IDEA文件中的依赖项的路径是绝对路径。如果手动定义指向Gradle依赖缓存的路径变量,IDEA将自动使用此路径变量替换绝对依赖路径。如果你使用这样的路径变量,你需要通过idea.pathVariables配置此路径变量,这样它就可以进行正确的合并而不重复创建。
The paths of the dependencies in the generated IDEA files are absolute. If you manually define a path variable pointing to the Gradle dependency cache, IDEA will automatically replace the absolute dependency paths with this path variable. If you use such a path variable, you need to configure this path variable via idea.pathVariables, so that it can do a proper merge without creating duplicates.