Skip to content

Don't run compilation on InteractiveDriver.run every time for the "same input" #17715

@tanishiking

Description

@tanishiking

Originally reported in scalameta/metals#4060

TL;DR

  • I'm looking for a way to cache the result of InteractiveDriver.run
  • I believe we can invalidate the cache by checking
    • input file isn't updated from the last compilation
    • there's no change in the classpath from the last compilation
  • If yes, where should I handle it? In InteractiveDriver or its consumers (like metals side)?
  • If no, what we need to check for cache invalidation?

Background

While I CPU profile Metals using async-profiler, I realized that Metals (in Scala3 project) invokes InteractiveDriver.run quite frequently, especially from HoverProvider.hover (that invokes when users hover on symbol in the editors).

This is because we invoke InteractiveDriver.run every time when Metals uses information from presentation compilers.
For example, HoverProvider.hover runs InteractiveDriver.run for every invocations anyway.
https://github.com/scalameta/metals/blob/eab3df81d3a55445f558c959ee5d2fa24308be82/mtags/src/main/scala-3/scala/meta/internal/pc/HoverProvider.scala#L39

It might be a big CPU saver if we don't run InteractiveDriver.run if the input is the same (that is likely to happen especially when we're code reading and doing hover or navigations).

A little bit of experiments

I tried to experiment, maybe we can cache invalidate by checking the combination of file content and runId scalameta/metals#4100

However, I realized that this work only if we edit files only in Metals.

// initial
// src/main/scala/A.scala
trait A {
  def foo: Int
}
// src/main/scala/B.scala
object B:
  val a: A = _
  val v = a.foo // call hover on `v` - should show `Int`
  
// then do changes in A.scala outside of Metals (which means there's no compilation in InteractiveDriver, but it should be compiled by the build server like bloop or sbt and classpath will be updated)
// src/main/scala/A.scala
trait A {
  def foo: String
}
// src/main/scala/B.scala
object B:
  val a: A = _
  val v@@ = a.foo // it should show `String` 
                  // but if there's a cache by file content of `B.scala` and `runId`
                  // it will show `Int` on hover.

We need to know if there are changes on the classpath. 🤔

Questions

  • I believe we can cache invalidate by the file content and checking there was a change on classpath that InteractiveDriver aware of, no?
  • If yes, where should we handle it, InteractiveDriver side or Metals side? (Or do you think we shouldn't cache it?)
  • If no, what we should check in addition to the classpath and content?

Any opinions are welcome!

BTW, I heard that someone is already taking a look at this topic, maybe @jchyb ? (sorry if I mistaken)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions