博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
IntelliJ IDEA:结构搜索和替换
阅读量:2509 次
发布时间:2019-05-11

本文共 24459 字,大约阅读时间需要 81 分钟。

Modern IDEs are very powerful tools that can help developers in all kinds of situations. Unfortunately, much of this power is often lost because many functions remain unknown to developers, hiding in the shadows.

现代IDE是非常强大的工具,可以在各种情况下帮助开发人员。 不幸的是,许多功能通常会丢失,因为许多功能对于开发人员仍然是未知的,藏在阴影中。

这些功能之一的简单示例 (Simple example of the one of the such functions)

Did you know that when you press F2 in IntelliJ IDEA, the cursor will jump to the nearest error in the file? And in the absence of an error – to the nearest warning? It seems that this is a secret only a few people know about.

您是否知道在IntelliJ IDEA中按F2时,光标将跳至文件中最接近的错误? 在没有错误的情况下–最接近的警告是? 似乎只有少数人知道这是一个秘密。

Structural search and replace is one such pair of features. They can be extremely useful in situations where a whole variety of other functions can’t quite get the job done.

结构搜索和替换就是这样的一对功能。 在其他各种功能无法完全完成工作的情况下,它们非常有用。

In this post, I will present some of these situations and go beyond artificial cases by demonstrating examples of real code from two projects:

在本文中,我将通过演示两个项目的真实代码示例来介绍其中的一些情况,并超越人工案例:

  1. 3D-engine for game development, , which is an example of a big, interesting project.

    游戏开发的3D引擎 ,是一个有趣的大型项目的示例。

  2. My own pet project, , where I experiment with compiling into native executable code using .

    我自己的宠物项目, ,在那里我与编译实验到使用本机的可执行代码 。

In fact, it is this second project that encouraged me to write this post but I’m getting ahead of myself. First things first...

实际上,正是第二个项目鼓励了我写这篇文章,但是我超越了自己。 首先是...

一个简单的任务 (A simple task)

Before we start looking at Structural Search, let’s consider some simple tasks where this search could be useful. Here is an example of one of my recent tasks, using a of a jMonkeyEngine project as the code for demonstration (rather than closed source code). This task requires me to search for open lock objects using the synchronized keyword (see "Item 82 – Document thread safety", from chapter 11, "Concurrency", in Joshua Bloch’s ).

在开始查看结构化搜索之前,让我们考虑一些简单的任务,这些搜索可能会有用。 这是我最近的一项任务的示例,使用jMonkeyEngine项目的作为演示代码(而不是封闭的源代码)。 该任务要求我使用synchronized关键字搜索打开锁对象(请参见Joshua Bloch的第11章“并发性”中的“ Item 82 –文档线程安全性”)。

The point is that using synchronization for objects that are publicly available is not a great idea. In this case, control over synchronization is lost and third-party code may start interfering with it, which could lead to undesirable effects and eventually to deadlocks.

关键是,对公开可用的对象使用同步并不是一个好主意。 在这种情况下,将失去对同步的控制,并且第三方代码可能会开始干扰它,从而可能导致不良后果并最终导致死锁。

It is important to bear in mind that synchronized keyword has two use cases:

重要的是要记住, synchronized关键字有两个用例:

As a method modifier:

作为方法修饰符:

class ClassA {    public synchronized void someMethod() {        // ...    }}

And as an internal method structure:

并作为内部方法结构:

class ClassA {    public void someMethod() {        synchronized(this) {            // ...        }    }}

In fact, these two examples demonstrate how synchronization works in an open object. It would be correct to write the following code:

实际上,这两个示例演示了同步如何在打开的对象中工作。 编写以下代码是正确的:

class ClassA {    private final Object sync = new Object();    public void someMethod() {        synchronized(sync) {            // ...        }    }}

In this example, no third-party code can interfere with synchronization.

在此示例中,任何第三方代码都不会干扰同步。

But how can you tell if there is such a pattern in the project code?

但是,如何确定项目代码中是否存在这种模式?

The easiest way is to do a full-text search for the synchronized keyword and carefully analyze each occurrence of it. But such an approach is only good for small projects. If we try the same search in jMonkeyEngine, we’ll find an overwhelming 117 occurrences. The word “synchronized” appears not only as the structure we’re looking for, but also in comments and text strings. And it can be rather tedious to deal with so many occurrences.

最简单的方法是对synchronized关键字进行全文搜索,并仔细分析它的每次出现。 但是这种方法仅对小型项目有用。 如果在jMonkeyEngine中尝试相同的搜索,我们将发现117个事件。 “同步”一词不仅出现在我们想要的结构中,而且还出现在注释和文本字符串中。 处理如此多次的事件可能非常繁琐。

So what can be done? This is where Structural Search and Replace in IntelliJ IDEA help.

那该怎么办呢? 这是IntelliJ IDEA帮助中的结构搜索和替换的地方。

结构搜索:基本原理 (Structural Search: the fundamentals)

First, let’s look at the action in IntelliJ IDEA.

首先,让我们看一下IntelliJ IDEA中的动作。

Open the jMonkeyEngine project and call the Structural Search window (Edit -> Find -> Search Structurally...), which has two areas:

打开jMonkeyEngine项目,然后调用“结构搜索”窗口( Edit -> Find -> Search Structurally... ),该窗口有两个区域:

  1. Search template text area.

    搜索模板文本区域。
  2. Filter setting area.

    过滤器设置区域。

So how is this different from a typical search?

那么,这与典型搜索有何不同?

In contrast to a regular search, where we just look for the occurrence of some substring (directly or via regular expression), in this search we look for a structural template in the programming or markup language (up to HTML).

与常规搜索相反,在常规搜索中,我们只是查找某个子字符串的出现(直接或通过正则表达式),而在此搜索中,我们则以编程或标记语言(最多HTML)查找结构模板。

In general, if you want to see examples of different searches you can run using this tool, you can use an expandable catalog of examples. To do this, you just need to open the menu under the wrench icon and choose the Existing Templates... item:

通常,如果要查看可以使用此工具运行的不同搜索的示例,则可以使用示例的可扩展目录。 为此,您只需要打开扳手图标下的菜单,然后选择“ Existing Templates...项:

A window with a wealth of ready templates opens:

将打开一个包含大量现成模板的窗口:

This catalog contains a lot of examples that are useful for both studying and practical use. But we’ve decided we want to look specifically for synchronization points.

该目录包含许多示例,这些示例对于学习和实际使用都是有用的。 但是我们已经决定要专门针对同步点。

Let’s begin with the case where the synchronized keyword is used as a method modifier, because in this case synchronization occurs based on a public object, simply by design. We’ll enter the following structure into the left field:

让我们从使用synchronized关键字作为方法修饰符的情况开始,因为在这种情况下,仅基于设计,同步是基于公共对象发生的。 我们将在左侧字段中输入以下结构:

synchronized $type$ $method$ ($ptype$ $param$) {    $statement$;}

Here we are defining a search pattern. We are not searching for an exact match, but rather a set of syntax constructions that meet the criterion.

在这里,我们定义一个搜索模式。 我们不是在寻找精确匹配,而是在寻找一组符合条件的语法构造。

You can see many tokens in this template limited by $ symbols at the front and at the back. These are template variables. If the token is set without $ symbols (like synchronized, for instance), it should be present in the discovered code snippet as-is. And if the token is inside the dollar signs, then by default anything can be in that position. It doesn’t matter what is there exactly.

您可以在此模板的前面和后面看到许多令牌,这些令牌受$符号限制。 这些是模板变量。 如果令牌,而没有设置$符号(如synchronized ,例如),它应该是存在于发现的代码片段原样。 如果令牌位于美元符号内,则默认情况下任何位置都可以位于该位置。 到底有什么无关紧要。

The names of the variables inside the dollars signs can be arbitrary. They can be used later on for setting additional criteria in the right window area if necessary.

美元符号内的变量名称可以是任意的。 以后可以使用它们在必要时在右侧窗口区域中设置其他条件。

So, if we look at the template in an abstract way, we can understand that this is a search for an arbitrary method definition, with some return type (including void), some single parameter, and a single line in the method body. And above all, this method is synchronizable.

因此,如果我们以抽象的方式查看模板,我们可以理解,这是对任意方法定义的搜索,该方法定义在方法主体中具有某些返回类型(包括void ),某些单个参数和一行。 最重要的是,该方法是可同步的。

But what if we want to find all the synchronizable methods with an arbitrary quantity of parameters (including methods without parameters) and with an arbitrary compound body?

但是,如果我们想找到带有任意数量的参数(包括没有参数的方法)和任意复合主体的所有可同步方法,该怎么办?

This is where template variable filters come into play. These are set in the right part of the window.

这是模板变量过滤器起作用的地方。 这些设置在窗口的右侧。

I must admit, it took me quite a while to understand how to use these filters, until I figured out that they are context-dependent based on the input cursor position in the search template text area.

我必须承认,花了相当长的时间才了解如何使用这些过滤器,直到我发现它们基于上下文(取决于上下文)取决于输入模板在搜索模板文本区域中的位置。

For instance, if we highlight variable $param$ on the left, then on the right we may set the criterion for the number of parameter occurrences in the function declaration. For this purpose, it will be enough to click the Add Filter hyperlink and select the second menu item, Count.

例如,如果我们在左侧突出显示变量$param$ ,则在右侧我们可以为函数声明中的参数出现次数设置标准。 为此,单击Add Filter超链接并选择第二个菜单项Count就足够了。

In the filter line that appears, we want to specify that the number of parameters can be from 0 to infinity, so we leave the second field empty:

在出现的过滤器行中,我们要指定参数的数量可以从0到无穷大,因此我们将第二个字段保留为空:

Now the template will search for all the synchronizable methods with an arbitrary number of parameters, but with only one line of code in the body. We set exactly the same zero-to-infinity limitation for the $statement$ variable in order to correct this.

现在,模板将搜索具有任意数量的参数,但主体中只有一行代码的所有可同步方法。 为了更正此问题,我们为$statement$变量设置了完全相同的零到无穷大限制。

That’s it. Now you can press Find and see all the synchronizable methods in the project, which is 18 altogether:

而已。 现在,您可以按“ Find并查看项目中的所有可同步方法,共18种:

This is an amazing start!

这是一个了不起的开始!

顺其自然:脚本过滤器 (Down the rabbit hole: Script Filter)

What about using synchronization with arbitrary objects? At first glance, this seems to be even simpler:

如何对任意对象使用同步? 乍一看,这似乎更简单:

synchronized($Obj$) {    $statement$;}

But the problem is how to define that the $Obj$ variable is private? We can see from the pattern shown in the book that this should be a variable of the Object type. Then we can add a Type filter and set the Object class type name, provided that the conformance is strict, without regard to hierarchy (i.e. with no ticks in the checkboxes below the filter):

但是问题是如何定义$Obj$变量是私有的? 从书中所示的模式中我们可以看到,这应该是Object类型的变量。 然后,我们可以添加一个Type过滤器并设置Object类的类型名称,前提是一致性很严格,而无需考虑层次结构(即过滤器下方的复选框中没有任何滴答声):

This allows us to find the places where synchronization uses closed objects (but this still won’t find all of them!). This, in turn, will give us the opposite result of what we require. In some particular cases, this could be useful if we need to find all synchronizations for objects of a certain type, and even for descendants of this type (if we tick the checkbox with type hierarchy below the filter).

这使我们能够找到同步使用封闭对象的位置(但是仍然找不到所有对象!)。 反过来,这将给我们带来与我们要求相反的结果。 在某些特定情况下,如果我们需要查找某种特定类型的对象甚至该类型后代的所有同步(如果我们在过滤器下方选中with type hierarchy的复选框),这可能会很有用。

But in general, we would like to find the cases when a non-private object of any type is used.

但总的来说,我们希望找到使用任何类型的私有对象的情况。

Unfortunately, there is no simple way to find those. The only thing that can help us is the Script type constraint — the most powerful, but also the most complicated, structural search function.

不幸的是,没有找到这些的简单方法。 唯一可以帮助我们的是脚本类型约束-功能最强大,但结构最复杂的搜索功能。

The point is that arbitrary code can be written as a Groovy function that returns true or false, and variables from the search pattern defined in $ symbols will be used as parameters along with a pair of service variables: __context__ and __log__.

的一点是,任意代码可以写为一个Groovy函数返回truefalse ,和变量从所限定的搜索模式$符号将被用作与一对服务变量的沿参数: __context____log__

This is the part I find the most upsetting. These variables represent the objects of PSI parse tree elements, yet at the same time:

这是我最难过的部分。 这些变量表示PSI解析树元素的对象,但同时:

  • The script entry field (all of a sudden) .

    脚本输入字段(突然之间) 。

  • It is almost impossible to guess exactly which PSI tree element will turn out to be the variable.

    几乎不可能确切地猜出哪个PSI树元素将成为变量。
  • There is no reference to the PSI tree structure; the only way to understand what I can to do with the PSI is the sources of PSI itself.

    没有引用PSI树结构; 理解我对PSI可以做什么的唯一方法是PSI本身的来源。
  • The general usability of the filer is awful. You simply have to accept that it contracts when the input focus is lost and that you need to expand the field each time and change the size of the elements to see the full script.

    文件管理器的一般可用性很差。 您只需要接受输入焦点丢失时它就会收缩,并且每次都需要扩展该字段并更改元素的大小以查看完整的脚本即可。

So what do we need to do? Let’s get into it. First of all, we need to understand what value appears in the $Obj$ variable. The best option I came up with was to use the regular Groovy function: println. We add the Script type filter to the $Obj$ variable and enter the following text:

那么,我们需要做什么? 让我们开始吧。 首先,我们需要了解$Obj$变量中出现了什么值。 我想到的最好的选择是使用常规的Groovy函数: println 。 我们将Script类型过滤器添加到$Obj$变量,然后输入以下文本:

println(Obj)return true

As we can see, names of variables can be used in the script, but without dollar signs. While performing this search, we will see all the occurrences of synchronized in the code. However, we are not interested in this, we want to see the logs that were printed using println.

如我们所见,变量名称可以在脚本中使用,但是没有美元符号。 在执行此搜索时,我们将在代码中看到所有出现的synchronized 。 但是,我们对此不感兴趣,我们希望查看使用println打印的日志。

And they were printed to the log of IntelliJ IDEA itself. You can find it via the menu: Help->Show Log in.... As I have KDE, the full name of the menu item is Show Log in Dolphin. As a result, the system file manager will open, specifying the actual log file. This is where we should look to find information about the object of interest to us. In this case, we can find the following lines:

然后将它们打印到IntelliJ IDEA本身的日志中。 您可以通过菜单找到它: Help->Show Log in... 因为我有KDE,所以菜单项的全名是Show Log in Dolphin 。 结果,系统文件管理器将打开,并指定实际的日志文件。 在这里我们应该寻找有关我们感兴趣的对象的信息。 在这种情况下,我们可以找到以下几行:

2020-07-05 15:03:00,998 [14151177] INFO - STDOUT - PsiReferenceExpression:pending2020-07-05 15:03:01,199 [14151378] INFO - STDOUT - PsiReferenceExpression:source2020-07-05 15:03:01,216 [14151395] INFO - STDOUT - PsiThisExpression:this2020-07-05 15:03:01,219 [14151398] INFO - STDOUT - PsiReferenceExpression:receiveObjectLock2020-07-05 15:03:01,222 [14151401] INFO - STDOUT - PsiReferenceExpression:invoke2020-07-05 15:03:01,226 [14151405] INFO - STDOUT - PsiReferenceExpression:chatServer2020-07-05 15:03:01,231 [14151410] INFO - STDOUT - PsiReferenceExpression:obj2020-07-05 15:03:01,236 [14151415] INFO - STDOUT - PsiReferenceExpression:sync2020-07-05 15:03:01,242 [14151421] INFO - STDOUT - PsiReferenceExpression:image2020-07-05 15:03:01,377 [14151556] INFO - STDOUT - PsiClassObjectAccessExpression:TerrainExecutorService.class2020-07-05 15:03:01,409 [14151588] INFO - STDOUT - PsiReferenceExpression:byteBuffer2020-07-05 15:03:01,429 [14151608] INFO - STDOUT - PsiReferenceExpression:lock2020-07-05 15:03:01,432 [14151611] INFO - STDOUT - PsiReferenceExpression:eventQueue2020-07-05 15:03:01,456 [14151635] INFO - STDOUT - PsiReferenceExpression:sensorData.valuesLock2020-07-05 15:03:01,593 [14151772] INFO - STDOUT - PsiReferenceExpression:createdLock2020-07-05 15:03:01,614 [14151793] INFO - STDOUT - PsiReferenceExpression:taskLock2020-07-05 15:03:01,757 [14151936] INFO - STDOUT - PsiReferenceExpression:loaders2020-07-05 15:03:01,765 [14151944] INFO - STDOUT - PsiReferenceExpression:threadLock

So, we see that objects of at least three types can serve as the Obj value:

因此,我们看到至少三种类型的Obj可以用作Obj值:

  • PsiThisExpression — token this;

    PsiThisExpression —标记为this

  • PsiClassObjectAccessExpression — synchronization by object of type Class (synchronized (TerrainExecutorService.class) {...});

    PsiClassObjectAccessExpression —按Class类型的对象进行synchronized (TerrainExecutorService.class) {...} );

  • PsiReferenceExpression — some expression whose result is used as the synchronization object.

    PsiReferenceExpression —一些表达式,其结果用作同步对象。

The first two types can be determined automatically as open object synchronization. This means that, if we have Obj as the object of a PsiThisExpression or PsiClassObjectAccessExpression type, true should be returned.

前两种类型可以自动确定为开放对象同步。 这意味着,如果我们将Obj作为PsiThisExpressionPsiClassObjectAccessExpression类型的对象,则应返回true

But what can be done with the PsiReferenceExpression type?

但是,使用PsiReferenceExpression类型可以做什么?

Unfortunately, the only way to look for an answer to this question is to address the sources. As the Java parser from JetBrains is open and published on GitHub within IntelliJ IDEA sources, there is no reason not to open it up for a little look. The class we’re interested in is located .

不幸的是,寻找该问题答案的唯一方法是解决消息来源。 由于JetBrains的Java解析器是开放的,并在IntelliJ IDEA来源中的GitHub上发布,因此没有理由不对其进行稍微看看。 我们感兴趣的班级位于 。

I won’t trouble you with the details of rummaging through PSI sources. I will just show the resulting script:

我不会麻烦您通过PSI来源进行翻糖的细节。 我将显示结果脚本:

if (Obj instanceof com.intellij.psi.PsiThisExpression) return true;if (Obj instanceof com.intellij.psi.PsiClassObjectAccessExpression) return true;if (Obj instanceof com.intellij.psi.PsiReferenceExpression) {    def var = Obj.advancedResolve(false).element;    if (var instanceof com.intellij.psi.PsiParameter) return true;    if (var instanceof com.intellij.psi.PsiLocalVariable) {        return !(var.initializer instanceof com.intellij.psi.PsiNewExpression);    }    if (var instanceof com.intellij.psi.PsiField) {        return !var.hasModifier(com.intellij.lang.jvm.JvmModifier.PRIVATE) &&               !var.hasModifier(com.intellij.lang.jvm.JvmModifier.PROTECTED);    }}return true;

This search yields only 12 occurrences. The result obtained is not ideal, as some of these occurrences are false positives. But I think it is enough for this example. However, we can see that the Psi structure is very powerful and allows us to work with the code structure at a very high level of abstraction.

该搜索仅产生12次。 获得的结果并不理想,因为其中一些事件是误报。 但是我认为这个例子就足够了。 但是,我们可以看到Psi结构非常强大,并允许我们以很高的抽象水平使用代码结构。

您的第一次静态代码分析检查 (Your first static code analysis inspection)

As we saw a little earlier, looking some things up can turn out to be quite challenging. And it is frustrating to see so much effort going to waste. In general, it would be great for the IDE to give us an immediate prompt when we write something wrong.

正如我们之前看到的那样,查找某些内容可能会非常具有挑战性。 看到如此多的工作浪费了,这令人沮丧。 通常,当我们写错了东西时,IDE会给我们立即提示是很棒的。

This is another area where IntelliJ IDEA can help. It has plenty of inspections to help developers write more correct code by highlighting incorrect parts and explaining what is wrong. There, hidden in plain sight, is one remarkable inspection that is disabled by default. This is the .

这是IntelliJ IDEA可以提供帮助的另一个领域。 它具有大量检查功能,可通过突出显示不正确的部分并解释错误的内容来帮助开发人员编写更正确的代码。 在那里,隐藏在其中的是一项出色的检查,默认情况下会禁用它。 这是 。

You can find this inspection in the Settings window (File->Settings...->Editor->Inspections):

您可以在“设置”窗口中找到该检查( File->Settings...->Editor->Inspections ):

Enable this inspection and press the plus icon to add the structural search template. The results of your last search will appear in the search window by default. Press the OK button and enter the name of the search template you need, for instance, Open object sync (or something more descriptive).

启用此检查,然后按加号图标添加结构搜索模板。 默认情况下,上一次搜索的结果将显示在搜索窗口中。 按OK按钮,然后输入所需的搜索模板的名称,例如, Open object sync (或更具描述性的名称)。

That’s it. Now IntelliJ IDEA will start automatically highlighting all the parts of the code that fall within this search:

而已。 现在,IntelliJ IDEA将自动开始突出显示此搜索中的所有代码部分:

Presto!!! You’ve created your very first code inspection ever! Congrats! All that is left now is to commit it together with the project so that others can use it too. To do this, you just have to add the .idea/inspectionProfiles directory where these settings are stored in the version control system.

普雷斯托! 您已经创建了您的第一个代码检查! 恭喜! 现在剩下的就是将它与项目一起提交,以便其他人也可以使用它。 为此,您只需要添加.idea/inspectionProfiles目录,这些设置就存储在版本控制系统中。

结构替换 (Structural Replace)

Just like there is a structural search action, there is a structural replace function in IntelliJ IDEA (Edit -> Find -> Replace Structurally...):

就像有结构搜索动作一样,IntelliJ IDEA中有一个结构替换功能( Edit -> Find -> Replace Structurally... ):

In contrast to the Search Structurally window, one more area appears in this window when you open it. This allows you to set the replacement for parts of the project that match the template. As in the template, the replacement can contain variables limited by dollar signs on both sides. These variables can be set up similarly in the filter entry field. But, in this case, these are not going to be filtration terms. Instead, they are exclusively Groovy scripts for computing replacement text instead of the variable.

与“按结构搜索”窗口相反,当您打开该窗口时,该窗口中还会出现一个区域。 这使您可以为与模板匹配的项目部分设置替换项。 就像模板中一样,替换项可以包含两侧均受美元符号限制的变量。 可以在过滤器输入字段中类似地设置这些变量。 但是,在这种情况下,这些将不是过滤术语。 相反,它们仅是用于计算替换文本而非变量的Groovy脚本。

I also would like to provide a practical example here. I had to replace all calls for the functions of one kind:

我也想在这里提供一个实际的例子。 我不得不替换所有对一种功能的调用:

classInitializationSupport.initializeAtRunTime(WindowPropertyGetter.class, AWT_SUPPORT);

in a with calls of all kinds:

在具有各种要求的 :

classInitializationSupport.initializeAtRunTime("sun.awt.X11.WindowPropertyGetter", AWT_SUPPORT);

I had to remove the explicit use of classes in the code, and later I had to do the same with respect to several kinds of functions.

我不得不在代码中删除对类的显式使用,后来我不得不对几种函数进行相同的操作。

The last thing I wanted was to try to do it all manually. So instead I used Replace Structurally. First, I specified the search template:

我想做的最后一件事是尝试手动完成所有操作。 所以我改为使用Replace Structurally 。 首先,我指定了搜索模板:

classInitializationSupport.initializeAtRunTime($Clazz$.class, AWT_SUPPORT)

I imposed no limitations for the $Clazz$ variable, as I did not care which class would appear there.

我没有对$Clazz$变量施加任何限制,因为我不在乎哪个类会出现在这里。

Then I set the replacement:

然后我设置替换:

classInitializationSupport.initializeAtRunTime("$FullClass$", AWT_SUPPORT)

And this is where I needed to calculate the new value of the $FullClass$ variable. For this, I highlighted it with the cursor and set the following script in the limitations entry field:

这是我需要计算$FullClass$变量的新值的$FullClass$ 。 为此,我用光标突出显示了它,并在限制输入字段中设置了以下脚本:

Clazz.type.canonicalText

This script allows us to take the type that falls within the variable from the $Clazz$ search template, obtain the full name of this type, and insert into the method parameter as a line.

该脚本使我们能够从$Clazz$搜索模板中获取属于该变量的类型,获取该类型的全名,并作为一行插入到method参数中。

The resulting Structural Replace window looks as follows:

出现的“结构替换”窗口如下所示:

Then we press Find and get a list of the possible replacements:

然后,按“ Find并获得可能的替换列表:

Here we can view every occurrence and what it turns into (using Preview Replacement). We can also exclude any occurrences (from the context menu) or make transformations in one go (with Replace All).

在这里,我们可以查看所有事件及其发生的结果(使用“ Preview Replacement )。 我们还可以排除所有出现的事件(从上下文菜单中)或一次性进行转换(使用Replace All )。

Ultimately, once you’ve got a feel for PSI, it’s not that difficult.

最终,一旦您对PSI有所了解,就没有那么困难了。

结构替换为意图 (Structural Replace as Intention)

Now it is time to bring up another powerful IntelliJ IDEA tool – Intentions. Intentions allow us to use code transformations immediately at the cursor location.

现在该提出另一个强大的IntelliJ IDEA工具Intentions 。 意图使我们可以在光标位置立即使用代码转换。

For instance, if you write code that looks like:

例如,如果您编写如下代码:

public static void main(String[] args) {    List
list = Arrays.asList(0, 10, 20, 30); for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); }}

and then place the cursor on for and press Alt+Enter, you will receive the suggestion to replace this for loop with an alternative implementation. For instance, to transform it into a for each loop:

然后将光标放在for ,然后按Alt+Enter ,您将收到建议将for循环替换为其他实现。 例如,将其转换为for each loop

public static void main(String[] args) {    List
list = Arrays.asList(0, 10, 20, 30); for (Integer integer : list) { System.out.println(integer); }}

This means that the same intention can be implemented with Structural Replace. To do this, you can just expand on the same inspection that we set for the Structural Search. But we should specify when adding a new item that it is not going to be a search but rather a replacement:

这意味着可以用“ Structural Replace实现相同的目的。 为此,您可以仅对我们为“结构搜索”设置的同一检查进行扩展。 但是,当添加新项目时,我们应该指定它不是搜索而是替代:

Now the respective areas in the code will be highlighted and autocorrection will be suggested there:

现在,代码中的各个区域将突出显示,并在此处建议自动更正:

Congratulations once again! You've made the first Intention in your life!

再次恭喜! 您已经有了人生的第一个意图!

结论 (Conclusion)

In this post, I tried to help you familiarize yourself with one of the most complex, but at the same time one of the most powerful, functions in IntelliJ IDEA. Yes, not everything about this function is convenient right now. However, there are cases where what you need to do is not possible by any other means.

在这篇文章中,我试图帮助您熟悉IntelliJ IDEA中最复杂的功能之一,但同时又是最强大的功能之一。 是的,有关此功能的所有信息现在都不方便。 但是,在某些情况下,您无法通过其他任何方式来做。

I haven’t described these functions in full detail. Loads of aspects remain beyond the scope of this post. But I hope this has provided enough of a foundation for you to get started and perhaps think about diving deeper for yourself.

我没有详细描述这些功能。 方面的负载仍然超出了本文的范围。 但是我希望这为您提供了足够的基础,让您开始起步,或者考虑自己更深入地学习。

Good luck!

祝好运!

P.S. Great thanks to Anton Arhipov for awesome help with this translation!

PS非常感谢Anton Arhipov在翻译出色帮助!

翻译自:

转载地址:http://azdwd.baihongyu.com/

你可能感兴趣的文章
CentOs7安装rabbitmq
查看>>
(转))iOS App上架AppStore 会遇到的坑
查看>>
解决vmware与主机无法连通的问题
查看>>
做好产品
查看>>
项目管理经验
查看>>
笔记:Hadoop权威指南 第8章 MapReduce 的特性
查看>>
JMeter响应数据出现乱码的处理-三种解决方式
查看>>
获取设备实际宽度
查看>>
Notes on <High Performance MySQL> -- Ch3: Schema Optimization and Indexing
查看>>
Alpha冲刺(10/10)
查看>>
数组Array的API2
查看>>
为什么 Redis 重启后没有正确恢复之前的内存数据
查看>>
No qualifying bean of type available问题修复
查看>>
第四周助教心得体会
查看>>
spfile
查看>>
Team Foundation Service更新:改善了导航和项目状态速查功能
查看>>
WordPress资源站点推荐
查看>>
Python性能鸡汤
查看>>
android Manifest.xml选项
查看>>
Cookie/Session机制具体解释
查看>>