Skip to content

Commit

Permalink
Move error annotation logic in the editor. Report errors as you type …
Browse files Browse the repository at this point in the history
…won't miss

updates anymore.

Fixed #84. Relevant question on SOF: http://stackoverflow.com/questions/12507620/race-conditions-in-annotationmodel-error-annotations-lost-in-reconciler

(cherry picked from b8a11c9)
  • Loading branch information
dragos authored and dotta committed Oct 18, 2012
1 parent 9da288d commit e14cd98
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import scalariform.ScalaVersions
import scala.tools.eclipse.ScalaHover
import org.scalaide.worksheet.ScriptCompilationUnit

class ScriptConfiguration(val pluginPreferenceStore: IPreferenceStore, textEditor: ITextEditor) extends SourceViewerConfiguration {
class ScriptConfiguration(val pluginPreferenceStore: IPreferenceStore, textEditor: ScriptEditor) extends SourceViewerConfiguration {
@inline private def scalaPreferenceStore: IPreferenceStore = ScalaPlugin.prefStore

private val javaColorManager = JavaPlugin.getDefault.getJavaTextTools.getColorManager
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ import org.scalaide.worksheet.editor.action.RunEvaluationAction
import org.eclipse.swt.SWT
import scala.tools.eclipse.InteractiveCompilationUnit
import scala.tools.eclipse.ui.InteractiveCompilationUnitEditor
import org.eclipse.jdt.core.compiler.IProblem
import org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitDocumentProvider.ProblemAnnotation
import org.eclipse.jface.text.Position
import scala.collection.JavaConverters
import org.eclipse.jface.text.source.IAnnotationModelExtension
import scala.tools.eclipse.util.SWTUtils

object ScriptEditor {

Expand Down Expand Up @@ -184,8 +190,10 @@ class ScriptEditor extends TextEditor with SelectionTracker with ISourceViewerEd
addAction(menu, ITextEditorActionConstants.GROUP_EDIT, "format")
}

type IAnnotationModelExtended = IAnnotationModel with IAnnotationModelExtension with IAnnotationModelExtension2

/** Return the annotation model associated with the current document. */
private def annotationModel: IAnnotationModel with IAnnotationModelExtension2 = getDocumentProvider.getAnnotationModel(getEditorInput).asInstanceOf[IAnnotationModel with IAnnotationModelExtension2]
private def annotationModel: IAnnotationModelExtended = getDocumentProvider.getAnnotationModel(getEditorInput).asInstanceOf[IAnnotationModelExtended]

def selectionChanged(selection: ITextSelection) {
import scala.collection.JavaConverters.asScalaIteratorConverter
Expand All @@ -194,7 +202,7 @@ class ScriptEditor extends TextEditor with SelectionTracker with ISourceViewerEd
setStatusLineErrorMessage(msg)
}

def getViewer: ISourceViewer = getSourceViewer
def getViewer: ISourceViewer = getSourceViewer()

override protected def editorSaved(): Unit = {
super.editorSaved()
Expand Down Expand Up @@ -227,7 +235,30 @@ class ScriptEditor extends TextEditor with SelectionTracker with ISourceViewerEd
private def withScriptCompilationUnit(f: ScriptCompilationUnit => Unit): Unit = {
ScriptCompilationUnit.fromEditor(ScriptEditor.this) foreach f
}

override def getInteractiveCompilationUnit(): Option[InteractiveCompilationUnit] = ScriptCompilationUnit.fromEditor(this)


@volatile
private var previousAnnotations = List[ProblemAnnotation]()

def updateErrorAnnotations(errors: List[IProblem]) {
def position(p: IProblem) = new Position(p.getSourceStart, p.getSourceEnd - p.getSourceStart + 1)

val newAnnotations = for (e <- errors if !isPureExpressionWarning(e)) yield {
val annotation = new ProblemAnnotation(e, null) // no compilation unit
(annotation, position(e))
}

val newMap = newAnnotations.toMap
import JavaConverters._
annotationModel.replaceAnnotations(previousAnnotations.toArray, newMap.asJava)
previousAnnotations = newAnnotations.map(_._1)

// This shouldn't be necessary in @dragos' opinion. But see #84 and
// http://stackoverflow.com/questions/12507620/race-conditions-in-annotationmodel-error-annotations-lost-in-reconciler
SWTUtils.asyncExec { getSourceViewer.invalidateTextPresentation() }
}

private def isPureExpressionWarning(e: IProblem): Boolean =
e.getMessage == "a pure expression does nothing in statement position; you may be omitting necessary parentheses"
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
package org.scalaide.worksheet.reconciler

import scala.tools.eclipse.logging.HasLogger

import org.eclipse.jdt.core.compiler.IProblem
import org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitDocumentProvider.ProblemAnnotation
import org.eclipse.jface.text._
import org.eclipse.jface.text.reconciler._
import org.eclipse.jface.text.source._
import org.eclipse.ui.texteditor._
import org.scalaide.worksheet.ScriptCompilationUnit
import scala.collection.JavaConverters
import org.scalaide.worksheet.editor.ScriptEditor
import org.eclipse.ltk.internal.ui.refactoring.util.SWTUtil
import scala.tools.eclipse.util.SWTUtils

class ScalaReconcilingStrategy(textEditor: ITextEditor) extends IReconcilingStrategy with HasLogger {
class ScalaReconcilingStrategy(textEditor: ScriptEditor) extends IReconcilingStrategy with HasLogger {
private var document: IDocument = _
private lazy val annotationModel = textEditor.getDocumentProvider.getAnnotationModel(textEditor.getEditorInput)

private lazy val scriptUnit = ScriptCompilationUnit.fromEditor(textEditor).get // we know the editor is a Scala Script editor

override def setDocument(doc: IDocument) {
Expand All @@ -29,26 +30,9 @@ class ScalaReconcilingStrategy(textEditor: ITextEditor) extends IReconcilingStra
override def reconcile(partition: IRegion) {
val errors = scriptUnit.reconcile(document.get)

updateErrorAnnotations(errors)
}

private var previousAnnotations = List[ProblemAnnotation]()

private def updateErrorAnnotations(errors: List[IProblem]) {
def position(p: IProblem) = new Position(p.getSourceStart, p.getSourceEnd - p.getSourceStart + 1)

previousAnnotations.foreach(annotationModel.removeAnnotation)

for (e <- errors if !isPureExpressionWarning(e)) {
val annotation = new ProblemAnnotation(e, null) // no compilation unit
annotationModel.addAnnotation(annotation, position(e))
previousAnnotations ::= annotation
}
textEditor.updateErrorAnnotations(errors)
}

private def isPureExpressionWarning(e: IProblem): Boolean =
e.getMessage == "a pure expression does nothing in statement position; you may be omitting necessary parentheses"

/** Ask the underlying unit to reload on each document change event.
*
* This is certainly wasteful, but otherwise the AST trees are not up to date
Expand All @@ -62,6 +46,5 @@ class ScalaReconcilingStrategy(textEditor: ITextEditor) extends IReconcilingStra
}

override def documentAboutToBeChanged(event: DocumentEvent) {}

}
}

0 comments on commit e14cd98

Please sign in to comment.