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:
|
|
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
:
|
|
These results are pretty useless, because:
- it lists
static
methods which do not take a string - it lists
static
instances ofIcon
- 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!
Structural Search
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.
The empty dialog looks similar to this:
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.
Defining a Search
Icon
is an interface. We know that we’re looking for a method defined like this:
|
|
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.
Writing a script for structural search
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:
|
|
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!
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.
External Resources on Structural Search
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
- An older article on Structural Search by Maxim Mossienko of JetBrains: Structural Search and Replace: What, Why, and How-to. The UI changed since 2006, but most of details should still be relevant.
- An older article by Nadav Cohen: IntelliJ Gem: Structural Search.
- An article on structural search and replace: intellij structural replace.
- The oldest reference to structural search I could find: IntelliJ IDEA 6.0 “Demetra”: Structural Search Inspections