Improved *.gradle.kts IDE Support
We have significantly improved the IDE support for Gradle Kotlin DSL scripts (
*.gradle.kts files), and we’d like to share some details with you in this blog post. The changes will be publicly available in the Kotlin 1.3.70 release, but you can already try them by joining the Kotlin 1.3.70 EAP (Early Access Program).
Some of you may have experienced the following scenario. You have a project built using Gradle Kotlin DSL scripts. You open the
build.gradle.kts file to modify the project build logic, but suddenly your computer starts burning through CPU, your script looks like plain text (with no highlighting), and there’s no code assistance in the script. Then it takes up to 10 seconds for the script to get highlighted when it is opened for the first time.
Well, now the IDE highlights the script instantly if you open it for an already imported Gradle project!
What’s more, we’ve managed to speed up the highlighting and completion processes for scripts, which should be especially noticeable for large projects. Using various IDE features across multiple files, such as “Find Usages” in
buildSrc, should now work faster.
Make sure you have all the following versions to enjoy the new changes:
- Gradle version 6.0+
- IntelliJ IDEA version 2019.2+
- Kotlin plugin version 1.3.70+
If you use the default project wizard to create a new project, you can update the Gradle version by running
gradle wrapper --gradle-version=6.2. Starting with 2020.1, IntelliJ IDEA will automatically create a project with the Gradle version 6.0+.
Note that if you use the latest Kotlin plugin with an older version of Gradle, or an older version of IntelliJ IDEA, the script support will still work the old, slow way.
Now we’d like to share some technical details of how we achieved that. We’ll discuss how the IDE support worked before and what has changed.
The Gradle Kotlin DSL scripts contain regular Kotlin code, which means you can expect code assistance that’s typical for all Kotlin files. For example, you can navigate to the declaration of each used function call, and you can use completion to see what options are available in the current context. However, script files are different from regular Kotlin files, as a script file can specify dependencies and applied Gradle plugins, which influence what can be resolved inside the script.
For instance, when you apply the application plugin to the script, you can later configure it by using the
Obviously, you can’t use the
application function without applying the corresponding plugin first. Having such dependencies inside scripts is what makes the IDE support of script files different from and more complicated than supporting regular Kotlin source files.
All the information that is needed to resolve a script file is referred to as the Script Configuration. The Script Configuration mainly depends on the content of
plugins sections of the script. It contains such information as the list of libraries that can be used in the script, and the list of default imports. The IDE uses the Script Configuration to provide code assistance for script files, such as highlighting, completion, navigation, and refactorings.
For more detailed technical information about Script Configurations, please see the corresponding KEEP document about scripting support.
Redesigning the interaction with the Gradle Daemon
For Gradle scripts, the up-to-date Script Configuration is managed by the Gradle Daemon, a process that runs in the background and is responsible for all Gradle-related tasks and activities. To update the Script Configuration, the IDE code sends a request to the Gradle Daemon, which runs the build Configuration Phase, and after that the Gradle Daemon provides the updated configuration.
Before Kotlin 1.3.70, because of the lack of a better API both in Gradle and the IntelliJ Platform, the Script Configuration was requested and updated on each typing in the script. That’s why everything was so slow.
Starting with Kotlin 1.3.70, we’ve redesigned the whole mechanism of requesting the Script Configuration from the Gradle Daemon. Now it happens for the first time on Gradle Project Import, and then it is stored in the IDE. When you change
plugins sections it will be requested again. We are planning to introduce
Load Script Configurations action (more on that later), so this won’t happen automatically on such changes in the script.
This improvement became possible thanks to the new Gradle TAPI, which allows getting all script configurations in one request (thanks to the Gradle Team), and thanks to the new IntelliJ IDEA API, which allows requesting additional information from Gradle during project import (thanks to Vlad Soroka).
Note that we expect no additional overhead for ‘Project Import’. Gradle compiles all scripts on the ‘Project Configuration’ stage, so all the necessary information was already present in Gradle. They only added new ways to retrieve it.
Better error reporting
The new Gradle API made one more improvement possible, and that’s better error reporting. You’ve probably seen the following message: “This script caused build configuration to fail… Show Kotlin Gradle DSL Logs in Finder/Explorer”. You could only see the errors from the Gradle Daemon in the separate log files. Well, now the Gradle Daemon returns all the information about errors directly, and it will be shown to you as usual inside the
Build Tool Window.
Explicit ‘Load Script Configuration’ action
As discussed above, to achieve better performance we will no longer request the updated Script Configuration automatically. In Kotlin 1.3.60, after you add a new plugin, e.g.
application from our example above, first you need to wait for a while until the updated Script Configuration is loaded. The loading happens automatically, in the background, so you are notified when it’s loaded and you can apply the changes. You’ve probably seen the message: “There’s a new script context available. Apply context” (“script context” is what we now call “Script Configuration”). After you apply it, you can immediately use the
application function, which is completed and resolved thanks to the loaded Script Configuration.
We plan to remove the request to Gradle Daemon after typing in
plugin sections: you’ll need to explicitly load the updated Script Configuration after you’ve added a new plugin. In IntelliJ IDEA 2019.2, you’ll use the standard “Import Gradle Project” action to do this. Starting with IntelliJ IDEA 2020.1, we’ll have a more user-friendly action, “Load Gradle Changes”, which does the same: it loads all the changes of the Gradle project structure.
We also plan to introduce one more action that can be used to load only the changes to the Script Configuration, without updating the whole project. We expect it to have the following workflow:
- You add the plugin (e.g. `application`).
- The IDE notices the changes in the corresponding script sections that may influence the Script Configuration and suggests loading it (it should still be loaded from the Gradle Daemon). You see the “Load Script Configurations” action, but no requests to the Gradle Daemon are sent until you explicitly request it.
- Only after you click this action, or the full “Load Gradle Changes” action, the changes are loaded and you start getting code assistance for the freshly added plugin, for example, you can complete the `application` function in the script.
We haven’t implemented these last changes yet, but this should happen soon. This means there’s still time for you to give us your feedback. Are the described workflow and the new “Load Script Configuration” action clear enough? We want to know what you think!