Class KotlinPsi

java.lang.Object
com.vaadin.copilot.kotlinrewriter.psi.KotlinPsi

public final class KotlinPsi extends Object
Parses Kotlin source into a PSI tree.

The only class in Copilot permitted to import org.jetbrains.kotlin.*. Everything else works through

invalid reference
KtNodeRef
and the rewriter interfaces, so the parser stays replaceable and the shade filter has one place to be checked against.

Uses the lightweight PSI path — CoreApplicationEnvironment plus a KotlinParserDefinition — and deliberately not KotlinCoreEnvironment.createForProduction, which loads the whole compiler frontend and its extension registry for no benefit here: Copilot needs exact source offsets, not resolved types. Types come from reflection on the live component instead.

Measured on this path: ~240 ms one-off environment setup, ~1.2 ms to parse a typical view, ~2 MB retained heap. See kotlin-support/PHASE-0-FINDINGS.md.

  • Method Details

    • parse

      public static org.jetbrains.kotlin.psi.KtFile parse(String fileName, String text)
      Parses Kotlin source text.

      The returned tree must not be retained past the operation that asked for it: it holds the whole PSI graph alive, and this class keeps its environment for the lifetime of the JVM.

      Parameters:
      fileName - the file's name, used only for messages and to pick the parser
      text - the source
      Returns:
      the parsed file
    • inPsi

      public static <T> T inPsi(KotlinPsi.PsiWork<T> work) throws IOException
      Runs work that reads a parse tree, with no other such work running.

      IntelliJ PSI is not thread safe, and a tree is not independent of the others: every file parsed here shares one project environment and one PsiManager, so two threads walking two different files still touch shared state. parse(java.lang.String, java.lang.String) being synchronized only makes parsing safe — it says nothing about the traversal that follows, which is where nearly all of the time is spent.

      That matters because Copilot genuinely does this concurrently. get-properties is dispatched asynchronously (CopilotSession#handleMessageAsync) while the property commands run on the caller's thread, so selecting a component can have the panel reading a Kotlin view on one thread while an edit rewrites one on another.

      One lock for all of it, rather than one per file, because the shared state is the environment rather than the tree. The cost is small — a parse is about 1 ms and a property lookup less — and the alternative is a data race whose symptom would be a corrupted tree in a developer's IDE.

      Type Parameters:
      T - what the work produces
      Parameters:
      work - the work to run
      Returns:
      its result
      Throws:
      IOException - if the work cannot read a source