Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Graceful handling of parsing errors from template compiler

* Because the Play template compiler throws an exception as soon as a parsing error is
encountered, calls to the Play template compiler are now wrapped in a ``Try``.
It's the responsability of the calling code to handle the failure case as it
makes sense.

* Use ``Annotation`` for reporting both parsing and compilation errors from
template files. The implication of this is that compilation  errors are no longer
displayed in the Problem View or the Package Explorer.

* Because we now solely use Annotation, we no longer need to use the Eclipse
extension point for defining new markers (look at the deleted lines in
plugin.xml).

* Finally, I'm aware that the code could use some more clean-up, but my intention
was to minimize changes as we plan to release 0.1 possibly next week already.

Fixes #29
  • Loading branch information...
commit be83b7f38b778e7bbfb28bf2b4f0cc844e0f9de1 1 parent b3b3270
@dotta dotta authored
Showing with 253 additions and 271 deletions.
  1. +21 −8 org.scala-ide.play2.tests/src/org/scalaide/play2/templateeditor/lexical/TemplateCompilationUnitTest.scala
  2. +5 −0 org.scala-ide.play2.tests/test-workspace/aProject/app/views/scala_compiler_error.scala.html
  3. +5 −0 org.scala-ide.play2.tests/test-workspace/aProject/app/views/template_parse_error.scala.html
  4. +0 −35 org.scala-ide.play2/plugin.xml
  5. +4 −5 org.scala-ide.play2/src/org/scalaide/play2/PlayPlugin.scala
  6. +1 −1  org.scala-ide.play2/src/org/scalaide/play2/PlayProject.scala
  7. +30 −49 org.scala-ide.play2/src/org/scalaide/play2/templateeditor/TemplateCompilationUnit.scala
  8. +33 −22 org.scala-ide.play2/src/org/scalaide/play2/templateeditor/TemplateEditor.scala
  9. +35 −8 org.scala-ide.play2/src/org/scalaide/play2/templateeditor/compiler/CompilerUsing.scala
  10. +54 −64 org.scala-ide.play2/src/org/scalaide/play2/templateeditor/compiler/TemplatePresentationCompiler.scala
  11. +15 −11 org.scala-ide.play2/src/org/scalaide/play2/templateeditor/completion/CompletionProposalComputer.scala
  12. +4 −2 org.scala-ide.play2/src/org/scalaide/play2/templateeditor/hover/TemplateHover.scala
  13. +29 −20 org.scala-ide.play2/src/org/scalaide/play2/templateeditor/hyperlink/LocalTemplateHyperlinkComputer.scala
  14. +9 −3 org.scala-ide.play2/src/org/scalaide/play2/templateeditor/hyperlink/TemplateDeclarationHyperlinkDetector.scala
  15. +8 −43 org.scala-ide.play2/src/org/scalaide/play2/templateeditor/reconciler/TemplateReconcilingStrategy.scala
View
29 org.scala-ide.play2.tests/src/org/scalaide/play2/templateeditor/lexical/TemplateCompilationUnitTest.scala
@@ -1,13 +1,13 @@
package org.scalaide.play2.templateeditor.lexical
+import scala.tools.eclipse.testsetup.SDTTestUtils
import scala.tools.eclipse.testsetup.TestProjectSetup
-import org.junit.Test
-import org.junit.Assert._
-import org.scalaide.play2.templateeditor.TemplateCompilationUnit
+
import org.eclipse.jdt.internal.core.util.SimpleDocument
-import scala.tools.eclipse.testsetup.FileUtils
import org.junit.AfterClass
-import scala.tools.eclipse.testsetup.SDTTestUtils
+import org.junit.Assert._
+import org.junit.Test
+import org.scalaide.play2.templateeditor.TemplateCompilationUnit
object TemplateCompilationUnitTest extends TestProjectSetup("aProject", bundleName = "org.scala-ide.play2.tests") {
@AfterClass
@@ -35,23 +35,36 @@ class TemplateCompilationUnitTest {
val document = new TestDocument(content1)
templateCU.connect(document)
- val generated1 = templateCU.generatedSource.content
+ val generated1 = templateCU.generatedSource.get.content
val content2 = "@(messages: String)\n<html><body>@messages</body></html>\n"
document.set(content2)
- val generated2 = templateCU.generatedSource.content
+ val generated2 = templateCU.generatedSource.get.content
assertFalse("generated1 and generated2 should be different", generated1 == generated2)
document.set(content1)
- val generated1_2 = templateCU.generatedSource.content
+ val generated1_2 = templateCU.generatedSource.get.content
assertEquals("Same content should return the same generated code", generated1, generated1_2)
}
+ @Test
+ def no_scala_source_is_generated_when_there_are_template_parse_errors() {
+ val tFile = file("app/views/template_parse_error.scala.html")
+ val tu = TemplateCompilationUnit(tFile)
+ assertTrue(tu.generatedSource.isFailure)
+ }
+
+ @Test
+ def scala_source_is_generated_when_there_are_scala_compiler__errors() {
+ val tFile = file("app/views/scala_compiler_error.scala.html")
+ val tu = TemplateCompilationUnit(tFile)
+ assertTrue(tu.generatedSource.isSuccess)
+ }
}
/**
View
5 org.scala-ide.play2.tests/test-workspace/aProject/app/views/scala_compiler_error.scala.html
@@ -0,0 +1,5 @@
+@(message: String)
+
+@main("Welcome") {
+ <p>@foo</p>
+}
View
5 org.scala-ide.play2.tests/test-workspace/aProject/app/views/template_parse_error.scala.html
@@ -0,0 +1,5 @@
+@(message: String)
+
+@main("Welcome") {
+ <p>@</p>
+}
View
35 org.scala-ide.play2/plugin.xml
@@ -73,41 +73,6 @@
</initializer>
</extension>
<extension
- point="org.eclipse.ui.editors.annotationTypes">
- <type
- markerSeverity="2"
- markerType="org.scala-ide.play2.templateProblem"
- name="org.scala-ide.play2.error"
- super="org.eclipse.ui.workbench.texteditor.error">
- </type>
- <type
- markerSeverity="1"
- markerType="org.scala-ide.play2.templateProblem"
- name="org.scala-ide.play2.warning"
- super="org.eclipse.ui.workbench.texteditor.warning">
- </type>
- <type
- markerSeverity="0"
- markerType="org.scala-ide.play2.templateProblem"
- name="org.scala-ide.play2.info"
- super="org.eclipse.ui.workbench.texteditor.info">
- </type>
- </extension>
- <extension
- id="templateProblem"
- name="Template Problem"
- point="org.eclipse.core.resources.markers">
- <super
- type="org.eclipse.core.resources.problemmarker">
- </super>
- <super
- type="org.eclipse.core.resources.textmarker">
- </super>
- <persistent
- value="false">
- </persistent>
- </extension>
- <extension
point="org.eclipse.core.contenttype.contentTypes">
<content-type
base-type="org.eclipse.core.runtime.text"
View
9 org.scala-ide.play2/src/org/scalaide/play2/PlayPlugin.scala
@@ -10,7 +10,6 @@ object PlayPlugin {
@volatile var plugin: PlayPlugin = _
private final val PluginId = "org.scala-ide.play2"
- final val ProblemMarkerId = PluginId + ".templateProblem"
final val RouteFormatterMarginId = PluginId + ".routeeditor.margin"
final val TemplateExtension = "scala.html"
@@ -24,14 +23,14 @@ object PlayPlugin {
class PlayPlugin extends AbstractUIPlugin {
import PlayPlugin._
override def start(context: BundleContext) = {
- super.start(context);
- PlayPlugin.plugin = this;
+ super.start(context)
+ PlayPlugin.plugin = this
initializeProjects()
}
override def stop(context: BundleContext) = {
- PlayPlugin.plugin = null;
- super.stop(context);
+ PlayPlugin.plugin = null
+ super.stop(context)
}
def asPlayProject(project: IProject): Option[PlayProject] = {
View
2  org.scala-ide.play2/src/org/scalaide/play2/PlayProject.scala
@@ -16,7 +16,7 @@ class PlayProject private (val scalaProject: ScalaProject) {
op(presentationCompiler)
}
- def withSourceFile[T](tcu: TemplateCompilationUnit)(op: (SourceFile, ScalaPresentationCompiler) => T): T = {
+ def withSourceFile[T](tcu: TemplateCompilationUnit)(op: (SourceFile, ScalaPresentationCompiler) => T): Option[T] = {
withPresentationCompiler { compiler =>
compiler.withSourceFile(tcu)(op)
}
View
79 org.scala-ide.play2/src/org/scalaide/play2/templateeditor/TemplateCompilationUnit.scala
@@ -1,40 +1,38 @@
package org.scalaide.play2.templateeditor
-import java.io.File
+import java.io.PrintStream
+
import scala.tools.eclipse.InteractiveCompilationUnit
import scala.tools.eclipse.ScalaPlugin
import scala.tools.eclipse.ScalaPresentationCompiler
-import scala.tools.eclipse.resources.MarkerFactory
-import scala.tools.eclipse.util.EclipseFile
+import scala.tools.eclipse.logging.HasLogger
import scala.tools.eclipse.util.EclipseResource
import scala.tools.nsc.interactive.Response
import scala.tools.nsc.io.AbstractFile
+import scala.tools.nsc.io.VirtualFile
import scala.tools.nsc.util.BatchSourceFile
import scala.tools.nsc.util.SourceFile
+import scala.util.Try
+
import org.eclipse.core.resources.IFile
-import org.eclipse.core.resources.IMarker
-import org.eclipse.core.resources.IResource
import org.eclipse.jdt.core.compiler.IProblem
import org.eclipse.jface.text.IDocument
-import org.eclipse.jface.text.Position
+import org.eclipse.jface.text.IRegion
import org.eclipse.jface.text.Region
import org.eclipse.ui.IEditorInput
import org.eclipse.ui.part.FileEditorInput
-import org.eclipse.ui.texteditor.ITextEditor
import org.scalaide.play2.PlayPlugin
import org.scalaide.play2.PlayProject
-import org.scalaide.play2.templateeditor.compiler.PositionHelper
-import scala.tools.nsc.io.VirtualFile
-import java.io.PrintStream
-import org.eclipse.jface.text.IRegion
import org.scalaide.play2.templateeditor.compiler.CompilerUsing
+import org.scalaide.play2.templateeditor.compiler.PositionHelper
+
import play.templates.GeneratedSourceVirtual
/** A Template compilation unit connects the presentation compiler
* view of a tmeplate with the Eclipse IDE view of the underlying
* resource.
*/
-case class TemplateCompilationUnit(val workspaceFile: IFile) extends InteractiveCompilationUnit {
+case class TemplateCompilationUnit(val workspaceFile: IFile) extends InteractiveCompilationUnit with HasLogger {
private var document: Option[IDocument] = None
@@ -71,7 +69,7 @@ case class TemplateCompilationUnit(val workspaceFile: IFile) extends Interactive
override def getContents: Array[Char] = {
withSourceFile({ (sourceFile, compiler) =>
sourceFile.content
- })()
+ })(null)
}
/** Return contents of template file
@@ -116,61 +114,46 @@ case class TemplateCompilationUnit(val workspaceFile: IFile) extends Interactive
}
override def withSourceFile[T](op: (SourceFile, ScalaPresentationCompiler) => T)(orElse: => T = scalaProject.defaultOrElse): T = {
- playProject.withSourceFile(this)(op)
- }
-
- def clearBuildErrors(): Unit = {
- workspaceFile.deleteMarkers(PlayPlugin.ProblemMarkerId, true, IResource.DEPTH_INFINITE)
- }
-
- def reportBuildError(errorMsg: String, start: Int, end: Int, line: Int): Unit = {
- reportBuildError(errorMsg, new Position(start, end - start + 1), line)
- }
-
- def reportBuildError(errorMsg: String, position: Position, line: Int): Unit = {
- def positionConvertor(position: Position, line: Int) = {
- MarkerFactory.RegionPosition(position.offset, position.length, line)
- }
- val pos = positionConvertor(position, line)
- TemplateProblemMarker.create(workspaceFile, IMarker.SEVERITY_ERROR, errorMsg, pos)
+ playProject.withSourceFile(this)(op) getOrElse (orElse)
}
/** maps a region in template file into generated scala file
*/
- def mapTemplateToScalaRegion(region: IRegion) = {
- synchronized {
- val offset = mapTemplateToScalaOffset(region.getOffset())
- val end = mapTemplateToScalaOffset(region.getOffset() + region.getLength() - 1)
- new Region(offset, end - offset + 1)
- }
+ def mapTemplateToScalaRegion(region: IRegion): Option[IRegion] = synchronized {
+ for {
+ start <- mapTemplateToScalaOffset(region.getOffset())
+ end <- mapTemplateToScalaOffset(region.getOffset() + region.getLength() - 1)
+ } yield new Region(start, end - start + 1)
}
/** maps an offset in template file into generated scala file
*/
- def mapTemplateToScalaOffset(offset: Int) = {
- playProject.withPresentationCompiler { pc =>
- val gen = generatedSource()
- PositionHelper.mapSourcePosition(gen.matrix, offset)
+ def mapTemplateToScalaOffset(offset: Int): Option[Int] = synchronized {
+ for(genSource <- generatedSource().toOption) yield {
+ playProject.withPresentationCompiler { pc =>
+ PositionHelper.mapSourcePosition(genSource.matrix, offset)
+ }
}
}
/** Return the offset in the template file, given an offset in the generated source file.
* It is the inverse of `mapTemplateToScalaOffset`. */
- def templateOffset(generatedOffset: Int): Int = {
- generatedSource().mapPosition(generatedOffset)
+ def templateOffset(generatedOffset: Int): Option[Int] = synchronized {
+ generatedSource().toOption.map(_.mapPosition(generatedOffset))
}
-
- private var cachedGenerated = generatedSource()
+ /* guarded by `this`*/
+ private var cachedGenerated: Try[GeneratedSourceVirtual] = generatedSource()
+ /* guarded by `this`*/
private var oldContents = getTemplateContents
/** Returns generated source of the given compilation unit.
*
* It caches results in order to save on (relatively expensive) calls to the template compiler.
*/
- def generatedSource(): GeneratedSourceVirtual = {
- if (oldContents != getTemplateContents) synchronized {
+ def generatedSource(): Try[GeneratedSourceVirtual] = synchronized {
+ if (oldContents != getTemplateContents) {
oldContents = getTemplateContents
- println("[generating template] " + getTemplateFullPath)
+ logger.debug("[generating template] " + getTemplateFullPath)
cachedGenerated = CompilerUsing.compileTemplateToScalaVirtual(getTemplateContents.toString(), file.file, playProject)
}
cachedGenerated
@@ -184,8 +167,6 @@ case class TemplateCompilationUnit(val workspaceFile: IFile) extends Interactive
}
-object TemplateProblemMarker extends MarkerFactory(PlayPlugin.ProblemMarkerId)
-
object TemplateCompilationUnit {
private def fromEditorInput(editorInput: IEditorInput): TemplateCompilationUnit = TemplateCompilationUnit(getFile(editorInput))
View
55 org.scala-ide.play2/src/org/scalaide/play2/templateeditor/TemplateEditor.scala
@@ -1,32 +1,24 @@
package org.scalaide.play2.templateeditor
+import scala.collection.JavaConverters
+import scala.tools.eclipse.ISourceViewerEditor
+import scala.tools.eclipse.InteractiveCompilationUnit
+import scala.tools.eclipse.ui.InteractiveCompilationUnitEditor
import scala.tools.eclipse.util.SWTUtils.fnToPropertyChangeListener
-import org.eclipse.jdt.internal.ui.text.java.hover.SourceViewerInformationControl
-import org.eclipse.jface.preference.IPreferenceStore
-import org.eclipse.jface.text.IInformationControlCreator
-import org.eclipse.jface.text.source.IOverviewRuler
-import org.eclipse.jface.text.source.IVerticalRuler
-import org.eclipse.jface.text.source.projection.ProjectionSupport
-import org.eclipse.jface.text.source.projection.ProjectionViewer
+
+import org.eclipse.jdt.core.compiler.IProblem
+import org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitDocumentProvider.ProblemAnnotation
+import org.eclipse.jface.text.Position
+import org.eclipse.jface.text.source.IAnnotationModel
+import org.eclipse.jface.text.source.IAnnotationModelExtension
+import org.eclipse.jface.text.source.IAnnotationModelExtension2
+import org.eclipse.jface.text.source.ISourceViewer
import org.eclipse.jface.util.IPropertyChangeListener
import org.eclipse.jface.util.PropertyChangeEvent
-import org.eclipse.swt.SWT
-import org.eclipse.swt.layout.FillLayout
-import org.eclipse.swt.layout.GridData
-import org.eclipse.swt.layout.GridLayout
-import org.eclipse.swt.widgets.Composite
-import org.eclipse.swt.widgets.Shell
import org.eclipse.ui.editors.text.EditorsUI
import org.eclipse.ui.editors.text.TextEditor
-import org.eclipse.ui.texteditor.AnnotationPreference
-import org.eclipse.ui.texteditor.MarkerAnnotationPreferences
-import org.scalaide.play2.PlayPlugin
-import scala.tools.eclipse.ISourceViewerEditor
-import org.eclipse.jface.text.source.ISourceViewer
import org.eclipse.ui.texteditor.ChainedPreferenceStore
-import org.eclipse.jdt.internal.ui.JavaPlugin
-import scala.tools.eclipse.ui.InteractiveCompilationUnitEditor
-import scala.tools.eclipse.InteractiveCompilationUnit
+import org.scalaide.play2.PlayPlugin
class TemplateEditor extends TextEditor with ISourceViewerEditor with InteractiveCompilationUnitEditor {
private lazy val preferenceStore = new ChainedPreferenceStore(Array((EditorsUI.getPreferenceStore()), PlayPlugin.prefStore))
@@ -38,7 +30,7 @@ class TemplateEditor extends TextEditor with ISourceViewerEditor with Interactiv
setDocumentProvider(documentProvider);
override def dispose() = {
- super.dispose();
+ super.dispose()
PlayPlugin.prefStore.removePropertyChangeListener(preferenceListener)
}
@@ -59,4 +51,23 @@ class TemplateEditor extends TextEditor with ISourceViewerEditor with Interactiv
override def getViewer: ISourceViewer = getSourceViewer
override def getInteractiveCompilationUnit(): InteractiveCompilationUnit = TemplateCompilationUnit.fromEditor(this)
+
+ @volatile
+ private var previousAnnotations: List[ProblemAnnotation] = Nil
+
+ private type IAnnotationModelExtended = IAnnotationModel with IAnnotationModelExtension with IAnnotationModelExtension2
+
+ /** Return the annotation model associated with the current document. */
+ private def annotationModel: IAnnotationModelExtended = getDocumentProvider.getAnnotationModel(getEditorInput).asInstanceOf[IAnnotationModelExtended]
+
+ def updateErrorAnnotations(errors: List[IProblem]) {
+ import scala.collection.JavaConverters._
+
+ def position(p: IProblem) = new Position(p.getSourceStart, p.getSourceEnd - p.getSourceStart + 1)
+
+ val newAnnotations = for (e <- errors) yield { (new ProblemAnnotation(e, null), position(e)) }
+
+ annotationModel.replaceAnnotations(previousAnnotations.toArray, newAnnotations.toMap.asJava)
+ previousAnnotations = newAnnotations.unzip._1
+ }
}
View
43 org.scala-ide.play2/src/org/scalaide/play2/templateeditor/compiler/CompilerUsing.scala
@@ -1,12 +1,20 @@
package org.scalaide.play2.templateeditor.compiler
+import java.io.File
+
+import scala.util.Failure
+import scala.util.Try
+
+import org.scalaide.play2.PlayProject
+
+import play.templates.GeneratedSourceVirtual
+
import play.templates.ScalaTemplateCompiler
import play.templates.ScalaTemplateCompiler._
-import java.io.File
-import play.templates.GeneratedSource
import play.templates.TemplateCompilationError
import scalax.file.Path
-import org.scalaide.play2.PlayProject
+
+
/**
* a helper for using template compiler
*/
@@ -29,16 +37,17 @@ import views.html._"""
* invokes compile method of template compiler and returns generated source object or
* in the case of error, returns appropriate exception
*/
- def compileTemplateToScalaVirtual(content: String, source: File, playProject: PlayProject) = {
+ def compileTemplateToScalaVirtual(content: String, source: File, playProject: PlayProject): Try[GeneratedSourceVirtual] = {
val sourcePath = playProject.sourceDir.getAbsolutePath()
if (source.getAbsolutePath().indexOf(sourcePath) == -1)
throw new Exception("Template files must locate in '" + sourcePath + "' or its subfolders!")
- try {
+
+ Try {
templateCompiler.compileVirtual(content, source, playProject.sourceDir, "play.api.templates.Html", "play.api.templates.HtmlFormat", additionalImports)
- } catch {
- case e @ TemplateCompilationError(source: File, message: String, line: Int, column: Int) =>
+ } recoverWith {
+ case TemplateCompilationError(source, message, line, column) =>
val offset = PositionHelper.convertLineColumnToOffset(content, line, column)
- throw new TemplateToScalaCompilationError(source, message, offset, line, column)
+ Failure(TemplateToScalaCompilationError(source, message, offset, line, column))
}
}
@@ -46,6 +55,24 @@ import views.html._"""
case class TemplateToScalaCompilationError(source: File, message: String, offset: Int, line: Int, column: Int) extends RuntimeException(message) {
override def toString = source.getName + ": " + message + offset + " " + line + "." + column
+
+ import org.eclipse.jdt.core.compiler.IProblem
+ import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities
+ import org.eclipse.jdt.internal.compiler.problem.DefaultProblem
+
+ def toProblem: IProblem = {
+ val severityLevel = ProblemSeverities.Error
+ new DefaultProblem(
+ source.getAbsolutePath().toCharArray,
+ message,
+ 0,
+ Array.empty[String],
+ severityLevel,
+ offset - 1,
+ offset - 1,
+ line,
+ column)
+ }
}
object PositionHelper {
View
118 org.scala-ide.play2/src/org/scalaide/play2/templateeditor/compiler/TemplatePresentationCompiler.scala
@@ -20,10 +20,14 @@ import scala.tools.eclipse.util.EclipseFile
import scala.tools.eclipse.util.EclipseResource
import scala.tools.eclipse.ScalaPresentationCompiler
import play.templates.GeneratedSourceVirtual
+import scala.tools.eclipse.logging.HasLogger
+import scala.util.Failure
+import scala.util.Success
+import scala.util.Try
/**
* presentation compiler for template files
*/
-class TemplatePresentationCompiler(playProject: PlayProject) {
+class TemplatePresentationCompiler(playProject: PlayProject) extends HasLogger {
/**
* A map between compilation units and associated batch source files
*/
@@ -33,7 +37,7 @@ class TemplatePresentationCompiler(playProject: PlayProject) {
* Returns scala batch source file (which is a virtual file) associated to
* the given generated source.
*/
- def scalaFileFromGen(gen: GeneratedSourceVirtual) = {
+ def scalaFileFromGen(gen: GeneratedSourceVirtual): BatchSourceFile = {
val fileName = gen.path
val file = ScalaFileManager.scalaFile(fileName)
new BatchSourceFile(file, gen.content)
@@ -43,68 +47,57 @@ class TemplatePresentationCompiler(playProject: PlayProject) {
* Returns scala batch source file (which is a virtual file) which is
* the result of compiling the given template compilation unit
*/
- def scalaFileFromTCU(tcu: TemplateCompilationUnit) = {
- val gen = tcu.generatedSource()
- scalaFileFromGen(gen)
+ def scalaFileFromTCU(tcu: TemplateCompilationUnit): Try[BatchSourceFile] = {
+ tcu.generatedSource() map scalaFileFromGen
}
private val scalaProject = playProject.scalaProject
def problemsOf(tcu: TemplateCompilationUnit): List[IProblem] = {
- try {
- val gen = tcu.generatedSource()
- val src = scalaFileFromGen(gen)
- val problems = scalaProject.withPresentationCompiler(pc => pc.problemsOf(src.file))()
- def mapOffset(offset: Int) = gen.mapPosition(offset)
- def mapLine(line: Int) = gen.mapLine(line)
- problems map (p => p match {
- // problems of the generated scala file
- case problem: DefaultProblem => new DefaultProblem(
- tcu.getTemplateFullPath.toCharArray(),
- problem.getMessage(),
- problem.getID(),
- problem.getArguments(),
- ProblemSeverities.Error,
- mapOffset(problem.getSourceStart()),
- mapOffset(problem.getSourceEnd()),
- mapLine(problem.getSourceLineNumber()),
- 1)
- })
- } catch {
- // template file could not be compiled to scala file. So now there is only a single
- // problem which is the thrown exception
- case TemplateToScalaCompilationError(source, message, offset, line, column) => {
- val severityLevel = ProblemSeverities.Error
- val p = new DefaultProblem(
- source.getAbsolutePath().toCharArray,
- message,
- 0,
- new Array[String](0),
- severityLevel,
- offset - 1,
- offset - 1,
- line,
- column)
- List(p)
- }
- // any other exception will be shown at first character of document
- case e: Exception => {
- val severityLevel = ProblemSeverities.Error
- val message = e.getMessage()
- val p = new DefaultProblem(
- tcu.getTemplateFullPath.toCharArray(),
- message,
- 0,
- new Array[String](0),
- severityLevel,
- 0,
- 1,
- 1,
- 1)
- List(p)
- }
+ tcu.generatedSource() match {
+ case Success(generatedSource) =>
+ val src = scalaFileFromGen(generatedSource)
+ val problems = scalaProject.withPresentationCompiler(pc => pc.problemsOf(src.file))()
+ def mapOffset(offset: Int) = generatedSource.mapPosition(offset)
+ def mapLine(line: Int) = generatedSource.mapLine(line)
+ problems map (p => p match {
+ // problems of the generated scala file
+ case problem: DefaultProblem => new DefaultProblem(
+ tcu.getTemplateFullPath.toCharArray(),
+ problem.getMessage(),
+ problem.getID(),
+ problem.getArguments(),
+ ProblemSeverities.Error,
+ mapOffset(problem.getSourceStart()),
+ mapOffset(problem.getSourceEnd()),
+ mapLine(problem.getSourceLineNumber()),
+ 1)
+ })
+
+ case Failure(parseError: TemplateToScalaCompilationError) =>
+ List(parseError.toProblem)
+
+ case Failure(error) =>
+ logger.error(s"Unexpected error while parsing template ${tcu.file.name}", error)
+ List(unknownError(tcu, error))
}
}
+
+ private def unknownError(tcu: TemplateCompilationUnit, error: Throwable): IProblem = {
+ val severityLevel = ProblemSeverities.Error
+ val message = error.getMessage()
+ new DefaultProblem(
+ tcu.getTemplateFullPath.toCharArray(),
+ message,
+ 0,
+ Array.empty[String],
+ severityLevel,
+ 0,
+ 1,
+ 1,
+ 1)
+ }
+
def askReload(tcu: TemplateCompilationUnit, content: Array[Char]) {
sourceFiles.get(tcu) match {
@@ -119,9 +112,8 @@ class TemplatePresentationCompiler(playProject: PlayProject) {
sourceFiles.put(tcu, tcu.batchSourceFile(content))
}
}
- try {
- val gen = tcu.generatedSource()
- val src = scalaFileFromGen(gen)
+ for(generatedSource <- tcu.generatedSource()) {
+ val src = scalaFileFromGen(generatedSource)
val sourceList = List(src)
scalaProject.withPresentationCompiler(pc => {
pc.withResponse((response: pc.Response[Unit]) => {
@@ -129,14 +121,12 @@ class TemplatePresentationCompiler(playProject: PlayProject) {
response.get
})
})()
- } catch {
- case _ =>
}
}
- def withSourceFile[T](tcu: TemplateCompilationUnit)(op: (SourceFile, ScalaPresentationCompiler) => T): T =
+ def withSourceFile[T](tcu: TemplateCompilationUnit)(op: (SourceFile, ScalaPresentationCompiler) => T): Option[T] =
scalaProject.withPresentationCompiler(pc => {
- op(scalaFileFromTCU(tcu), pc)
+ scalaFileFromTCU(tcu).map(op(_, pc)).toOption
})()
def destroy() = {
View
26 org.scala-ide.play2/src/org/scalaide/play2/templateeditor/completion/CompletionProposalComputer.scala
@@ -36,17 +36,21 @@ class CompletionProposalComputer(textEditor: ITextEditor) extends ScalaCompletio
private def findCompletions(viewer: ITextViewer, position: Int, tcu: TemplateCompilationUnit)(sourceFile: SourceFile, compiler: ScalaPresentationCompiler): List[ICompletionProposal] = {
val region = ScalaWordFinder.findCompletionPoint(tcu.getTemplateContents, position)
- val mappedRegion = tcu.mapTemplateToScalaRegion(region.asInstanceOf[Region])
- val mappedPosition = tcu.mapTemplateToScalaOffset(position - 1) + 1
-
- val res = findCompletions(mappedRegion)(mappedPosition, tcu)(sourceFile, compiler).sortBy(_.relevance).reverse
-
- res.map(prop => {
- val newProp = prop.copy(startPos = prop.startPos - mappedPosition + position)
-
- ScalaCompletionProposal(viewer.getSelectionProvider)(newProp)
- })
-
+
+ val completions = {
+ for {
+ mappedRegion <- tcu.mapTemplateToScalaRegion(region)
+ mappedPosition <- tcu.mapTemplateToScalaOffset(position - 1)
+ realPosition = mappedPosition + 1
+ } yield {
+ findCompletions(mappedRegion)(realPosition, tcu)(sourceFile, compiler).sortBy(_.relevance).reverse map { prop =>
+ val newProp = prop.copy(startPos = prop.startPos - realPosition + position)
+ ScalaCompletionProposal(viewer.getSelectionProvider)(newProp)
+ }
+ }
+ }
+
+ completions getOrElse Nil
}
def computeContextInformation(viewer: ITextViewer, offset: Int): Array[IContextInformation] = {
View
6 org.scala-ide.play2/src/org/scalaide/play2/templateeditor/hover/TemplateHover.scala
@@ -13,8 +13,10 @@ class TemplateHover(tcu: TemplateCompilationUnit) extends ScalaHover(tcu) {
override def getHoverInfo(viewer: ITextViewer, region: IRegion): String = {
// maps the region to scala generated source
- val mappedRegion = tcu.mapTemplateToScalaRegion(region.asInstanceOf[Region])
- super.getHoverInfo(viewer, mappedRegion)
+ tcu.mapTemplateToScalaRegion(region) match {
+ case Some(mappedRegion) => super.getHoverInfo(viewer, mappedRegion)
+ case None => null
+ }
}
}
View
49 org.scala-ide.play2/src/org/scalaide/play2/templateeditor/hyperlink/LocalTemplateHyperlinkComputer.scala
@@ -37,27 +37,36 @@ class LocalTemplateHyperlinkComputer extends AbstractHyperlinkDetector {
return Nil
val wordRegion = ScalaWordFinder.findWord(doc.get, currentSelection.getOffset).asInstanceOf[IRegion]
- val mappedRegion = icu.mapTemplateToScalaRegion(wordRegion)
- icu.withSourceFile { (source, compiler) =>
- import compiler._
- def localSymbol(sym: compiler.Symbol): Boolean = (
- (sym ne null) &&
- (sym ne NoSymbol) &&
- sym.pos.isDefined &&
- sym.pos.source == source)
+ icu.mapTemplateToScalaRegion(wordRegion) match {
+ case Some(mappedRegion) =>
+ icu.withSourceFile { (source, compiler) =>
+ import compiler._
+ def localSymbol(sym: compiler.Symbol): Boolean = {
+ (sym ne null) &&
+ (sym ne NoSymbol) &&
+ sym.pos.isDefined &&
+ sym.pos.source == source
+ }
- val pos = compiler.rangePos(source, mappedRegion.getOffset(), mappedRegion.getOffset(), mappedRegion.getOffset() + mappedRegion.getLength())
- val response = new Response[Tree]
- compiler.askTypeAt(pos, response)
- response.get match {
- case Left(tree: Tree) if localSymbol(tree.symbol) =>
- val sym = tree.symbol
- val offset = icu.templateOffset(sym.pos.startOrPoint)
- val hyper = Hyperlink.withText(sym.name.toString)(icu, offset, sym.name.length, sym.kindString + sym.nameString, wordRegion)
- List(hyper)
- case _ => Nil
- }
- }(Nil)
+ val pos = compiler.rangePos(source, mappedRegion.getOffset(), mappedRegion.getOffset(), mappedRegion.getOffset() + mappedRegion.getLength())
+ val response = new Response[Tree]
+ compiler.askTypeAt(pos, response)
+ response.get match {
+ case Left(tree: Tree) if localSymbol(tree.symbol) =>
+ val sym = tree.symbol
+ icu.templateOffset(sym.pos.startOrPoint) match {
+ case Some(offset) =>
+ val hyper = Hyperlink.withText(sym.name.toString)(icu, offset, sym.name.length, sym.kindString + sym.nameString, wordRegion)
+ List(hyper)
+ case None =>
+ Nil
+ }
+ case _ => Nil
+ }
+ }(Nil)
+
+ case None => Nil
+ }
}
if (textEditor == null) null // can be null if generated through ScalaPreviewerFactory
View
12 ...ala-ide.play2/src/org/scalaide/play2/templateeditor/hyperlink/TemplateDeclarationHyperlinkDetector.scala
@@ -21,6 +21,7 @@ import org.eclipse.jface.text.Region
import org.scalaide.play2.templateeditor.compiler.PositionHelper
import org.scalaide.play2.templateeditor.TemplateCompilationUnit
import scala.tools.eclipse.hyperlink.text.detector.DeclarationHyperlinkDetector
+import scala.tools.eclipse.util.Utils
class TemplateDeclarationHyperlinkDetector extends DeclarationHyperlinkDetector {
@@ -32,10 +33,15 @@ class TemplateDeclarationHyperlinkDetector extends DeclarationHyperlinkDetector
}
if (doc.getChar(currentSelection.getOffset()) == '.') // otherwise it will generate an error
return Nil
- val wordRegion = ScalaWordFinder.findWord(doc.get, currentSelection.getOffset).asInstanceOf[Region]
- val mappedRegion = icu.asInstanceOf[TemplateCompilationUnit].mapTemplateToScalaRegion(wordRegion)
+ val wordRegion = ScalaWordFinder.findWord(doc.get, currentSelection.getOffset)
+
+ import Utils.any2optionable
+ val tu = icu.asInstanceOfOpt[TemplateCompilationUnit]
- super.findHyperlinks(textEditor, icu, wordRegion, mappedRegion)
+ tu.flatMap(_.mapTemplateToScalaRegion(wordRegion)) match {
+ case Some(mappedRegion) => super.findHyperlinks(textEditor, icu, wordRegion, mappedRegion)
+ case None => Nil
+ }
}
}
View
51 org.scala-ide.play2/src/org/scalaide/play2/templateeditor/reconciler/TemplateReconcilingStrategy.scala
@@ -1,68 +1,34 @@
-package org.scalaide.play2.templateeditor
-package reconciler
+package org.scalaide.play2.templateeditor.reconciler
import scala.tools.eclipse.logging.HasLogger
-import org.eclipse.core.resources.IMarker
-import org.eclipse.jdt.core.IJavaModelMarker
-import org.eclipse.jdt.core.compiler.IProblem
-import org.eclipse.jdt.internal.core.builder.JavaBuilder
-import org.eclipse.jdt.internal.ui.javaeditor.JavaMarkerAnnotation
+
import org.eclipse.jface.text.DocumentEvent
import org.eclipse.jface.text.IDocument
import org.eclipse.jface.text.IDocumentListener
import org.eclipse.jface.text.IRegion
-import org.eclipse.jface.text.Position
import org.eclipse.jface.text.reconciler.DirtyRegion
import org.eclipse.jface.text.reconciler.IReconcilingStrategy
-import org.eclipse.ui.IFileEditorInput
-import org.eclipse.ui.texteditor.ITextEditor
-import org.eclipse.ui.texteditor.MarkerAnnotation
import org.scalaide.play2.templateeditor.TemplateCompilationUnit
-import org.scalaide.play2.PlayPlugin
-import org.eclipse.ui.texteditor.SimpleMarkerAnnotation
-import org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitDocumentProvider.ProblemAnnotation
-import org.eclipse.jface.text.source.IAnnotationModelExtension
+import org.scalaide.play2.templateeditor.TemplateEditor
-class TemplateReconcilingStrategy(textEditor: /*ITextEditor*/ TemplateEditor) extends IReconcilingStrategy with HasLogger {
+class TemplateReconcilingStrategy(textEditor: TemplateEditor) extends IReconcilingStrategy with HasLogger {
private var document: IDocument = _
- private lazy val annotationModel =
- textEditor.getDocumentProvider.getAnnotationModel(textEditor.getEditorInput).asInstanceOf[IAnnotationModelExtension]
private lazy val templateUnit = TemplateCompilationUnit.fromEditor(textEditor)
- def setDocument(doc: IDocument) {
+ override def setDocument(doc: IDocument) {
document = doc
doc.addDocumentListener(reloader)
}
- def reconcile(dirtyRegion: DirtyRegion, subRegion: IRegion) {
+ override def reconcile(dirtyRegion: DirtyRegion, subRegion: IRegion) {
logger.debug("Incremental reconciliation not implemented.")
}
- def reconcile(partition: IRegion) {
+ override def reconcile(partition: IRegion) {
val errors = templateUnit.reconcile(document.get)
-
- updateErrorAnnotations(errors)
- }
-
- @volatile
- private var previousAnnotations = List[ProblemAnnotation]()
-
- def createMarkerAnnotation(problem: IProblem) {
- templateUnit.reportBuildError(problem.getMessage(), problem.getSourceStart(), problem.getSourceEnd(), problem.getSourceLineNumber())
- }
-
- private def updateErrorAnnotations(errors: List[IProblem]) {
- import scala.collection.JavaConverters._
-
- def position(p: IProblem) =
- new Position(p.getSourceStart, p.getSourceEnd - p.getSourceStart + 1)
-
- val newAnnotations = for (e <- errors) yield { (new ProblemAnnotation(e, null), position(e)) }
-
- annotationModel.replaceAnnotations(previousAnnotations.toArray, newAnnotations.toMap.asJava)
- previousAnnotations = newAnnotations.unzip._1
+ textEditor.updateErrorAnnotations(errors)
}
/**
@@ -80,6 +46,5 @@ class TemplateReconcilingStrategy(textEditor: /*ITextEditor*/ TemplateEditor) ex
}
def documentAboutToBeChanged(event: DocumentEvent) {}
-
}
}
Please sign in to comment.
Something went wrong with that request. Please try again.