Skip to content
This repository was archived by the owner on Nov 10, 2018. It is now read-only.

Performance considerations

Tim Seckinger edited this page Jan 16, 2017 · 6 revisions

For each template insertion, IntelliJ will evaluate the macro multiple times, once to determine the completion suggestions (LookupElements) and (for some reason) four times to determine the Result to insert into the editor.
This can lead to long execution times, especially for complex scripts.

Multiple ways of dealing with this issue are highlighted below.

LookupElements-only execution

If you are fine with inserting nothing into the editor right away, but instead hitting the Return/Enter key to insert a completion option, you may want to skip Result calculation and only generate LookupElements. This can be accomplished using the _goal variable that will either be "RESULT" or "LOOKUP_ELEMENTS" to find out what the current evaluation is for.

Example (Groovy):

if (_goal == 'LOOKUP_ELEMENTS') {
    java.util.stream.LongStream.range(0, 999999999).sum() // Expensive operation
} else {
    ''
}

Caching

To avoid performing the same calculation multiple times during the same template insertion or across similar template insertions, it may prove useful to cache the return values of your script executions.

The plugin offers a caching mechanism to your scripts, accessible using the _cache variable. By default, no caching is performed, since no assumptions on your script’s behavior can me made.

Cache auto-loading

By calling _cache.store(Object) from your script, further macro evaluations (within the next 10 minutes) will load the stored object instead of running your script - including the ScriptEngine overhead - again, given that the following parameters are the same:

  • Your script’s effective language

  • Your script’s source code

  • The _args passed to your script

  • The _goal of the evaluation

  • The editor window the template is inserted into

The _goal being among these parameters means that your script will still be executed at least twice (once for each goal).
The store call returns the object you passed to it, so it can be used fluently in a return statement.

Example (Groovy):

return _cache.store(_args.join('+'))

If your script is impure because it has side effects or depends on parameters on top of those mentioned above (such as files, network data or the current date), skipping script execution by using caching may not be a good idea.
In the following example, the second template insertion would result in different values, but because caching is used, the same result will be displayed again:

return _cache.store(System.nanoTime())

Cache with metadata

You can pass any arbitrary number of parameters to _cache.store(Object…​). If you pass more than one, further parameters are considered metadata and the cached object will not be auto-loaded.

Instead, you can retrieve it later by calling cache.load(Object…​) with the same metadata in the same order, given that the following parameters are also the same:

  • Your script’s effective language

  • Your script’s source code

The load call will return an Optional containing your cached value, or an empty Optional if you did not store a value with the same metadata previously.

You can use this sort of caching for example if your expensive calculation is pure with regard to other factors that only the script knows. In the following example, this repo’s README will be downloaded from Github, but only if the master branch hasn’t changed since the last time this was done (although the performance benefit of caching in this example is debatable):

final contentUrl = ('https://raw.githubusercontent.com/jeysal/' +
        'livetemplate-scriptengine-macro/master/README.md').toURL()
final metadata = ('https://api.github.com/repos/jeysal/' +
        'livetemplate-scriptengine-macro/git/refs/heads/master').toURL().text

return _cache.load(metadata).orElseGet { _cache.store(contentUrl.text, metadata) }

Clone this wiki locally