Improved *.gradle.kts IDE Support

Posted on by Natalia Selezneva

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).

kotlin_blog_gradle_ide

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.

Script Configuration

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 application function:

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 buildscript and 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 buildscript or 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.

Future Plans

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 buildscript and 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:

  1. You add the plugin (e.g. application).
  2. 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.
  3. 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!

 

Comments below can no longer be edited.

12 Responses to Improved *.gradle.kts IDE Support

  1. Louis CAD says:

    February 19, 2020

    How are you going to handle errors like network errors or no internet after adding a new plugin or entry to the buildscript?

    Is that something that you’re testing in QA?

    • Sergey Rostov says:

      February 20, 2020

      Hi Louis!

      Not sure I understand your question.
      It will work the same way as by calling gradle from the console. The errors will be shown in “build” tool window. Actually, you can check it now by switching internet connection off, adding a new plugin in plugins section and calling “Reimport all Gradle Projects” in the Gradle tool window.

  2. ADEV says:

    February 19, 2020

    The extension is larger than the filename itself..

    Why not drop this UX non-sense and simply use:

    build.kt ?

    or better

    project.kt

    • ADEV says:

      February 19, 2020

      or even:

      build.kts
      or
      project.kts

    • Andrei Mishchenko says:

      February 19, 2020

      It wouldn’t be possible to distinguish Gradle scripts form other Kotlin or Kotlin script files in the project, so it wouldn’t be possible to provide Gradle specific classpath and completion, also In Gradle, you can have multiple files with .build.gradle suffix, and those will also have the same script definition as defauild build.gradle

      • ADEV says:

        February 19, 2020

        build.gradle.kt
        MyProject/
        ….src/
        ….build.gradle.kt
        MyLib/
        ….src/
        ….build.gradle.kt

        build.kts
        MyProject/
        ….src/
        ….build.kts
        MyLib/
        ….src/
        ….build.kts

        i don’t see a problem

        you can have multiple files with .build.gradle suffix

        build.kts -> default
        build.2.kts -> something else
        build_something -> something else

        Gradle and IDE can figure out what is what, there is no code outside of src

        • Andrei says:

          February 20, 2020

          there is no code outside of src
          There is no restrictions to have any other scripts or code on top level.
          ScriptDefinition for kotlin scripts is based on extension suffix, it allows to use different script definition for different scripts

          Anyway, it’s not even part of Idea implementation, it’s how Gradle works out of the box

  3. Jean-Michel says:

    February 20, 2020

    Nice work, I appreciate!

    If you allow me a rant, I’m very disappointed by the state of support not in IntelliJ but in Android Studio.

    It didn’t seem like the Android team at Google had a lot to do to support Gradle Kotlin DSL scripts, since 95% of the work was done upstream in IntelliJ and in Gradle.
    But as far as I’m concerned they did a really poor job of doing the remaining 5%.

    There this very basic issue that breaks the plugins mechanism on which the IDE integration is built that was opened in August 2017 by a Gradle employee who worked on this stuff. The impact of it is huge but the fix is quite small. What happened since? Nada

    Allow application of the Android Gradle plugins using Gradle’s plugins block
    https://issuetracker.google.com/issues/64551265

    Another low hanging fruit is that all plugins should provide documentatiion for both Groovy and Kotlin users. As you can see it, pretty much all plugin developers have done it except the Android guys
    https://github.com/gradle/gradle/issues/6790

    There are obvious bugs in Android Studio as well, like if you have a “settings.gradle.kts”, it will happily create an additional “settings.gradle” and breaks your build.

    At that point I’m not bothering to create issue on their bad issue tracker, because the real problem is that they don’t understand and/or don’t care about this enough to do some serious dogfooding

  4. bughunter says:

    February 20, 2020

    Will the new “Load Script Configuration” action also available for groovy scripts?

    • Nikita Skvortsov says:

      February 21, 2020

      Unfortunately no. This action only affect Kotlin scripts at the moment. But we may come up with similar solution for Groovy as well.

  5. Gabriel Feo says:

    February 21, 2020

    Can you clear up the difference between these three options, “Load script configuration”, “Re-import Gradle project” and “Sync project with Gradle files” (which seems to be Android-specific)?

    • “Load script configuration”: configures the project, loading script dependencies and caching everything for IDE features related to the scripts
    • “Sync project with Gradle files”: configures and builds the project, loading script and project dependencies and caching everything for IDE features related to the scripts and the source files
    • “Re-import Gradle project”: What “Sync project” does, but deleting what’s currently cached (?)

    • Sergey Rostov says:

      February 25, 2020
      • “Re-import Gradle project” and “Sync Project with Gradle files” do the same thing: they start the Gradle configuration phase, after which all project models will be collected (including script configuration, project dependencies, tasks, etc.). The IDE project structure will be updated in accordance with these models.
      • “Load script configuration” – runs the Gradle configuration phase and builds and loads Script configuration only without updating the project structure. This action is useful when your build scripts are not ready to update project structure, but you already need a new script configuration to continue script editing.

      In other words, “Load script configuration” is part of “Re-import Gradle project” / “Sync Project with Gradle files”.

Subscribe

Subscribe for updates