Structural Search in IntelliJ IDEA  Navigation

Structural Search in IntelliJ IDEA

This article explains how to to locate a method with a specific return type and a single string parameter.

Introduction

This is my first article in a new series. It’s meant to explore features in IntelliJ which I have never used so far. This way, I’ll learn a few new tricks and can write about at the same time.

This article is about the structural search. I knew for a long time that it’s there. I even tried to use it a couple years ago but didn’t understand how to use it.

Our use case

When I’m working with a large, unknown code base I frequently need to find which methods provide values of a certain type. intellij-community is sometimes like a jungle of unknowns.

For example, I want to retrieve an Icon by name. I have a String with the name, but still don’t know how to get it from the IntelliJ SDK.

Here’s an example:

1
2
3
4
5
public class MyClass {
    public static Icon loadIcon(String name) {
        // fixme: retrieve the Icon using a method from the SDK and return it
    }
}    

At first, I tried smart code completion to see which methods return an Icon. I had to invoke it twice to get a list of all values and methods assignable to icon:

1
2
3
    public static Icon loadIcon(String name) {
        Icon icon = // <-- call smart completion twice here 
    }
Result of calling smart completion twice

Result of calling smart completion twice

These results are pretty useless, because:

  • it lists static methods which do not take a string
  • it lists static instances of Icon
  • it’s an incredible long list

I’d love if IntelliJ was smart enough to figure out what I want. Even ranking methods taking a string to the top would be more useful than this.

Now this is where structural search comes to the rescue!

Finding it

Now, open the structural search interface. You can find it deeply nested in the menu at Edit -> Find -> Search Structurally …. Alternatively, press Ctrl + Shift + A (Help -> Find Action…) and enter Search struct to get to the action.

Looking up Search Structurally

Looking up Search Structurally

The empty dialog looks similar to this:

Empty dialog of Search Structurally

Empty dialog of Search Structurally

Using it

At first, explore the predefined searches to see what this thing offers. You can find these in the popup of the icon in the top right corner at Existing templates….

You write code with placeholders. Placeholders have filters. Your code is matched against your code base. Placeholders are matched against all occurrences.

For each placeholder you can define one of these filters (or a subset):

Text
A regular expression which is applied to the actual value of this placeholder. You can turn of the regular expression matching with a flag.
Count
Defines repetition of this placeholder. This is available for parameters, for example. This way you can define how many values this placeholder represents.
Reference
A reference to an existing template, either your own or a predefined one.
Script
A Groovy script.
Writing this script is difficult. By default you’ll get a line editor. You can expand it to show a few more lines. But you don’t get syntax highlighting or code completions to help you.

Icon is an interface. We know that we’re looking for a method defined like this:

1
public static IconOrImpl anyName(String anyName);

Therefore, the pattern for structural search is like this:

public static $ReturnType$ $Method$($ParameterType$ $Parameter$);

$ParameterType$ has to be limited to only match java.lang.String. Restricting the parameter is easy, we’re applying the text filter String here.

$ReturnType$ has to be restricted to the interface Icon or a class implementing it. This is difficult.

At first I tried to add a text filter with the regular expression javax\.swing\.Icon and with Apply withing type hierarchy checked. This did not work. The handbook isn’t even explaining what it does.

Then I spend a while to figure the script out. It operates on the $Method$ placeholder.

You need to create a snippet of Groovy code which accepts or rejects the current element. The last expression is of type boolean.

The script is operating on the internal structure of a file. This means that you access the parsed code in the form of a PSI tree.

If you’re not an experienced plugin developer, than figuring this out will be difficult. It took me more than an hour of frustrating trial and error to come up with the script. It doesn’t help if you don’t know Groovy :)

In the Groovy code, you have access to these variables:

  • __context__ is the element represented by your placeholder for some piece of code, which is matching your complete definition
  • __log__: I have no idea what this does.

__context__ in a script on $Method$ has the type com.intellij.psi.PsiMethod.

I had to try a few times to get meaningful error messages which told me about this. Now, its method getReturnType lets you access the return part of the method definition.

The IntelliJ SDK provides a few helpers to work with Java types:

  • the static method PsiType.getTypeByName locates a type by its fully qualified name.
  • the method PsiType#isAssignableFrom can be used to make sure that a variable of another type could be assigned

Now, here comes the final script for the $Method$ placeholder:

1
2
3
4
5
6
7
8
import com.intellij.psi.*
String fqn = "javax.swing.Icon";
PsiType target = PsiType.getTypeByName(
        fqn,
         __context__.getProject(), 
        __context__.getResolveScope()); 

target.isAssignableFrom(__context__.getReturnType())
Final version of the script in the Search Structurally dialog

Final version of the script in the Search Structurally dialog

Search result

Let’s run the search. The first thing you’ll notice that it’s slow. I used a custom scope to scan all of the IntelliJ SDK. After a while IntelliJ came up with this result set. This is so much better than the code completion popup!

Result of structural search displaying methods which take a String and return an Icon

Result of structural search displaying methods which take a String and return an Icon

Common mistakes

Library/SDK without attached sources

I wanted structural search to locate occurrences within the IntelliJ SDK. You can define a custom scope for this and use it for the structural search. But this wasn’t working as expected.

It turned out that structural search isn’t searching in compiled classes when no sources are attached. At least, results were inconsistent. The preview highlighted code in a decompiled file from the SDK, but the search couldn’t find it.

Attach the sources to the library and try again. You now should see the expected results.

Conclusion

Structural search is a very powerful feature of IntelliJ. I liked, that you can use it to very run complex queries across your codebase. It’s in IntelliJ for a very long time, at least since version 6. It’s probably one of the most underrated (and complex) features of your IDE.

I found it very hard to use. Writing scripts in this tiny editor is a pain. The help pages do not explain enough to help you define own queries or write scripts. It took me a long time to get this to work properly. Using this feature was some of the most frustrating experiences I had with IntelliJ in the last months. It reminded me of the time when I was still using Eclipse ;-)

A few quick idea to improve the interface are (not necessarily quick to implement):

  • allow to soft-wrap the main editor
  • a real code editor with syntax highlighting for the Groovy script
  • code completion for the Groovy script would be an incredible help here
  • support searches in Java libraries without attached sources

I did not explore structural replace yet.

Here are a few more resources if you’d like to learn more about it.

IntelliJ Help

The help pages only explain the basic functionality. It’s definitely not helping with the script part.

Misc Blogs and Articles