Where should I generate the stub tree?

I have a language plugin that is using my language file’s stub tree in a RubyLocalVariablesProvider (similar to a completion contributor). I have the stub tree working fine, but I can’t figure out the correct way to actually calculate the stub tree. I’ve run into problems with the IDE hanging in the past when incorrectly using the PSI tree.

Currently, I’m accessing the stub tree like so [1]:

    val properties = viewProvider.getRBladeFile().stub?.childrenStubs

    properties?.forEach {
      prop ->
      if (prop is RBladePropNameStub) {
        decs.put(prop.key, createPsiElement(parent.containingFile as RFile, prop.key, prop.textOffset))
      }
    }

In my PsiFileBase, I have added an override to getStubTree to calculate the tree [2]:

  override fun getStubTree(): StubTree? {
    return StubTreeLoader.getInstance().readOrBuild(project, virtualFile, this) as StubTree?
  }

It’s this readOrBuild() method which I think may not be suitable to run on the UI thread, but I have no idea. It’s something I’ve seen in other plugins.

[1] rblade-intellij/src/main/kotlin/com/mwnciau/rblade/ruby/RBladeRubyLocalVariablesProvider.kt at main · mwnciau/rblade-intellij · GitHub
[2] rblade-intellij/src/main/kotlin/com/mwnciau/rblade/psi/RBladeFile.kt at main · mwnciau/rblade-intellij · GitHub

You also need to store soft reference here, take a look at example in Angular plugin

1 Like

Thanks. ApplicationManager.getApplication().assertReadAccessAllowed() is marked as obsolete - do you know if it is needed or is there something else that should be done instead?

The equivalent ThreadingAssertions#softAssertReadAccess should be used when migrating existing code. However, in new code it’s better to use ThreadingAssertions#assertReadAccess()

It is mostly needed for correctness, and you can omit those

1 Like