26

Can somebody supply a minimal example of using the current scala presentation compiler (i.e. scala.tools.nsc.interactive.Global), which accomplishes the following tasks?

  • compile a single virtual source file (i.e. not in the file system but e.g. a String)
  • retrieve the resulting information for all phases from the compiler
  • propagate a change in the source file to the compiler
  • receive further information possibly asynchronously

There seems to be a lot of fluctuation in nsc and i couldn't find up to date small examples. So I would highly appreciate your help here.

Martin Ring
  • 5,404
  • 24
  • 47
  • 2
    Hi this link to eclipse plugin might helps https://github.com/scala-ide/scala-ide/blob/master/org.scala-ide.sdt.core/src/scala/tools/eclipse/ScalaPresentationCompiler.scala – Iraklis May 16 '13 at 13:15
  • @Iraklis That is indeed a helpful link. I looked at it before, but there are so many eclipse-specific features integrated, that I dont quite get it... I would like to use the presentation compiler in isolation. There is also that [blog post about ENSIME](http://ensime.blogspot.de/2010/08/building-ide-with-scala-presentation.html) but that is really outdated. – Martin Ring May 16 '13 at 13:29
  • 2
    You might also want to check the test on the interactive compiler https://github.com/scala/scala/tree/b380f9ecbe1be8ffaf0f32001e95566747017294/src/interactive/scala/tools/nsc/interactive/tests – Iraklis May 16 '13 at 14:14
  • @Iraklis that is truly a good reference! thank you! – Martin Ring May 16 '13 at 16:19

3 Answers3

10

Ok, one week of 100 bounty and still no answers so I will try it myself... Edits are very very welcome!

The key class to the presentation compiler is scala.tools.nsc.interactive.Global. So to start, we need to create an instance of the compiler.

import scala.tools.nsc.interactive.Global
class PresentationCompiler { // we want the compiler output to be virtual val target = new VirtualDirectory("", None)
// need to be adjusted in order to work with // sbt. See this question. val settings = new Settings() // output to virtual target settings.outputDirs.setSingleOutput(target)
// can be replaced by a custom instance // of AbstractReporter to gain control. val reporter = new ConsoleReporter(settings)
val compiler = new Global(settings, reporter)
... }

For the settings the link provided by Abhishek is very valuable.

But now for the interesting part:

1. compile a single virtual source file

To compile a String there is the possibility to create a BatchSourceFile with an underlying VirtualFile. The api is marked experimental here and seems patchy.

def compile(code: String) {
  val source = new BatchSourceFile("<virtual>", code)
  val response = new Response[Unit]
  compiler.askReload(List(source), response)    
  response.get.left.foreach { _ =>
    // success
  }
}

2. retrieve the resulting information for all phases from the compiler

This is the tricky part. Due to the multi-threaded nature of the compiler and the fact that flags are reused with different meanings across diferent phases, it is impossible to just get everything at once. Basically you will have to resort to the askSomething kind of methods which are documented in the API. For example:

val tcompletion = new Response[List[global.Member]]      
val pos = compiler.ask(() => new OffsetPosition(source, p))
global.askTypeCompletion(pos, tcompletion)
tcompletion.get(5000).get match {
  case Left(members) => // do something with members
  case Right(e) =>
    e.printStackTrace
}

3. propagate a change in the source file to the compiler

This is the interesting part, I wanted to find out about with this question. I really dont get this, because BatchSourceFile is described as a file which's Content does not change over time. So a custom implementation of SourceFile would need to be supplied?? Why would that not be in the interactive package. I am sure I just didn't catch something.

So my solution right now is to call the compile method again.

Community
  • 1
  • 1
Martin Ring
  • 5,404
  • 24
  • 47
2

Below link might help

https://github.com/twitter/util/blob/master/util-eval/src/main/scala/com/twitter/util/Eval.scala

I reckon it cater all the requirements you are looking for. :)

Abhishek kapoor
  • 151
  • 1
  • 1
  • 9
0

Might not be exactly what you are looking for, but one way to do it could be to:

  1. write the source you want to compile into a temporary file,

  2. run the compilation suffixing > output.tmp (*nix) operator to the command, which should output the compiler output into a file,

  3. read that file and load it into memory...

  4. and last you delete both tmp files.

I would hope this is not the only solution, but in case nothing else is possible, at least this should work...

FMaz008
  • 11,161
  • 19
  • 68
  • 100