Compiling and error reporting

During the last few days, I have been reinstating error reporting. Previously, EclipseFP would call GHC, collect its output in a Java string, and send that string off to Cohatoe (the now defunct bridge from Java to Haskell). A Haskell module would parse the GHC output using Parsec, return the result back to Java, where EclipseFP would do fancy things with the parsed output.

If this all sounds very roundabout, you’re right. We are already interfacing with Haskell in the form of the Scion server, so why not let it do the compiling and send us (EclipseFP) the result in a formatted way? Thoughts in this direction are under way, but currently hampered by a few factors:

  • Scion talks with the GHC API, which is single-threaded. This means that during background compiles, every Scion-based function in the IDE would stop working. It should be possible to fix this by letting Scion itself do background compilation, while still servicing requests that do not need to communicate with the GHC API directly, but this requires much work inside the Scion server.
  • Ideally, when a Cabal file is present, Scion should let Cabal do the building. Unfortunately, Cabal does not play well as a library, because errors will terminate the program. This has been fixed in the upcoming Cabal 1.8, to be released along with GHC 6.12.1 in September.
  • When a Cabal file is not present, Scion should do something along the lines of “ghc –make”. However, the make functionality is currently still interwoven with the compiler itself. A consequence is that compilation results are only available after all modules have been compiled. A callback for progress reports is in the works for GHC 6.12.1.

In the light of all these obstacles (especially the first one, which cannot be solved by installing a HEAD build of GHC) I decided to keep the current way of calling GHC as an external process. However, this meant that the output parsing had to be rewritten in Java. Moreover, because compiling a large project might take a while,  gathering up all output and processing it all at once is not the best approach. My new parser works directly on the output stream from the GHC process, so all errors will appear live in the IDE as soon as GHC spits them out.

With that in place, I sprinkled on some UI code (oh, this makes it sound so easy…) and got things working again. Like error squigglies:

You can hover over the error to get the message. Note that an error marker is also shown in the left margin, and next to the scrollbar. This last one is very convenient if you want to jump to an error in a large source file. These are the kinds of features that Eclipse gives you for free, which is the reason why I chose to do Haskell in Eclipse in the first place. Unfortunately, an error marker on the icon of the editor’s tab is more difficult, so I left that out for now.

The Project Explorer now also shows error markers on files that do not compile:

Parents of files with errors also receive this marker. This makes it easy to locate problems in a large project.

Errors are, as usual, also reported in a special Problems view:

It would be better if only the first line were shown, and could be expanded to show the additional information, but for a first attempt this will do.

If parsing of GHC’s output might fail for some reason, an error will be reported in the Error Log (which contains Eclipse errors, not Haskell errors), and you can always see the raw output in the Console:

Very nice.

There is, however, one small problem currently. This is caused by a mismatch between GHC’s notion of building, and Eclipse’s. When building, EclipseFP will visit all files in the project, then invoke “ghc –make” on them. It suffices to call “ghc –make” only on the file containing the main function, and GHC will take care of the rest. But not only is this approach wasteful: it will also lead to error markers appearing and disappearing as the same file gets compiled (as a dependency) multiple times. My plan for solving this is to add a project option to specify the main module and the name of the main function, so that we can call GHC only on that module. Much more efficient.

Leave a comment