Skip to content

Commit

Permalink
Merge pull request #1116 from sschaef/t1002726-clear-errors
Browse files Browse the repository at this point in the history
 Remove annotations that belong to a removed range of an editor
  • Loading branch information
kiritsuku committed Sep 5, 2016
2 parents aa54df1 + b25774f commit 81fd2c4
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 40 deletions.
@@ -1,28 +1,46 @@
package org.scalaide.ui.editor

import org.eclipse.jface.text.source.Annotation
import org.eclipse.jface.text.source.IAnnotationModel
import org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitDocumentProvider.ProblemAnnotation
import scala.collection.JavaConverters._
import scala.collection.breakOut

import org.eclipse.jdt.core.ICompilationUnit
import org.eclipse.jdt.core.compiler.IProblem
import org.scalaide.util.internal.eclipse.AnnotationUtils.RichModel
import org.eclipse.jface.text.Position
import org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitDocumentProvider.ProblemAnnotation
import org.eclipse.jface.text.ITextViewerExtension2
import org.eclipse.jdt.core.ICompilationUnit
import org.eclipse.jface.text.Position
import org.eclipse.jface.text.source.Annotation
import org.eclipse.jface.text.source.IAnnotationModelExtension2
import org.scalaide.util.internal.eclipse.AnnotationUtils._
import org.scalaide.util.ui.DisplayThread

trait DecoratedInteractiveEditor extends ISourceViewerEditor {

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

private var previousAnnotations = List[Annotation]()

/**
* This removes all annotations in the region between `start` and `end`.
*/
def removeAnnotationsInRegion(start: Int, end: Int): Unit = annotationModel foreach { model
val annsToRemove = model match {
case model: IAnnotationModelExtension2
model.getAnnotationIterator(start, end - start, /*canStartBefore*/ false, /*canEndAfter*/ false).asScala
case _
model.getAnnotationIterator.asScala.filter { ann
val pos = model.getPosition(ann)
pos.offset >= start && pos.offset + pos.length <= end
}
}
model.deleteAnnotations(annsToRemove.toSeq)
}

/**
* Update annotations on the editor from a list of IProblems
*/
def updateErrorAnnotations(errors: List[IProblem], cu: ICompilationUnit): Unit = annotationModel foreach { model
val newAnnotations: Map[Annotation, Position] = (for (e <- errors) yield {
val newAnnotations: Map[Annotation, Position] = (for (e errors) yield {
val annotation = new ProblemAnnotation(e, cu) // no compilation unit
val position = new Position(e.getSourceStart, e.getSourceEnd - e.getSourceStart + 1)
(annotation, position)
Expand All @@ -33,17 +51,21 @@ trait DecoratedInteractiveEditor extends ISourceViewerEditor {

// 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
val presViewer = getViewer
if (presViewer.isInstanceOf[ITextViewerExtension2]) {
getViewer match {
case viewer: ITextViewerExtension2
// TODO: This should be replaced by a better modularization of semantic highlighting PositionsChange
val newPositions = newAnnotations.values
def end (x:Position) = x.offset + x.length - 1
val taintedBounds : (Int, Int) = ((Int.MaxValue, 0) /: newPositions) {(acc, p1) => (Math.min(acc._1, p1.offset), Math.max(acc._2, end(p1)))}
val taintedLength = (taintedBounds._2 - taintedBounds._1 +1)
def end(x: Position) = x.offset + x.length - 1
val taintedBounds = (newPositions foldLeft (Int.MaxValue, 0)) { (acc, p1) (Math.min(acc._1, p1.offset), Math.max(acc._2, end(p1))) }
val taintedLength = (taintedBounds._2 - taintedBounds._1 + 1)

DisplayThread.asyncExec { presViewer.asInstanceOf[ITextViewerExtension2].invalidateTextPresentation(taintedBounds._1, taintedLength) }
} else {
DisplayThread.asyncExec { getViewer.invalidateTextPresentation() }
DisplayThread.asyncExec {
viewer.invalidateTextPresentation(taintedBounds._1, taintedLength)
}
case viewer
DisplayThread.asyncExec {
viewer.invalidateTextPresentation()
}
}
}

Expand Down
Expand Up @@ -254,7 +254,7 @@ class ScalaSourceViewerConfiguration(
else
baseReconcilers)

val reconciler = new ScalaReconciler(editor, s, isIncremental = false)
val reconciler = new ScalaReconciler(editor, s, isIncremental = true)
reconciler.setDelay(IScalaPlugin().getPreferenceStore.getInt(ScalaPreferences.ReconcilerDelayId))
reconciler.setProgressMonitor(new NullProgressMonitor())
reconciler
Expand Down
Expand Up @@ -16,11 +16,19 @@ class ScalaOutlineReconcilingStrategy(icuEditor: OutlinePageEditorExtension) ext
override def setProgressMonitor(pMonitor: IProgressMonitor): Unit = {}

override def reconcile(dirtyRegion: DirtyRegion, subRegion: IRegion): Unit = {
logger.debug("Incremental reconciliation not implemented.")
updateOutlineView()
}

override def reconcile(partition: IRegion): Unit = {
val sop = Option(icuEditor.getOutlinePage)
logger.debug("Non incremental reconciliation not implemented.")
}

override def initialReconcile(): Unit = {
updateOutlineView()
}

private def updateOutlineView(): Unit = {
val sop = Option(icuEditor.getOutlinePage)
if (!sop.isEmpty) {
val oldRoot = sop.get.getInput
icUnit.scalaProject.presentationCompiler.apply(comp => {
Expand All @@ -34,9 +42,4 @@ class ScalaOutlineReconcilingStrategy(icuEditor: OutlinePageEditorExtension) ext
})
}
}

override def initialReconcile(): Unit = {
reconcile(null)
}

}
}
Expand Up @@ -7,13 +7,14 @@ import org.eclipse.jdt.internal.ui.text.spelling.JavaSpellingProblem
import org.eclipse.jface.preference.IPreferenceStore
import org.eclipse.jface.text.IDocument
import org.eclipse.jface.text.IRegion
import org.eclipse.jface.text.reconciler.DirtyRegion
import org.eclipse.jface.text.source.IAnnotationModel
import org.eclipse.jface.text.source.ISourceViewer
import org.eclipse.ui.texteditor.ITextEditor
import org.eclipse.ui.texteditor.spelling.ISpellingProblemCollector
import org.eclipse.ui.texteditor.spelling.SpellingProblem
import org.eclipse.ui.texteditor.spelling.{SpellingReconcileStrategy => ESpellingReconcileStrategy}
import org.eclipse.ui.texteditor.spelling.{SpellingService => ESpellingService}
import org.eclipse.ui.texteditor.spelling.{ SpellingReconcileStrategy => ESpellingReconcileStrategy }
import org.eclipse.ui.texteditor.spelling.{ SpellingService => ESpellingService }

/**
* Reconcile strategy for spell checking. It checks whether spell checking is
Expand All @@ -22,7 +23,7 @@ import org.eclipse.ui.texteditor.spelling.{SpellingService => ESpellingService}
* The implementation of this class is adopted from
* [[JavaSpellingReconcileStrategy]], which couldn't be used because it sets the
* spelling service in its constructor. The spelling service is the only thing
* we had to modify to enable a Scala spelling engine, for a more detailful
* we had to modify to enable a Scala spelling engine, for a more detailed
* description see [[ScalaSpellingService]].
*
* @param editor
Expand Down Expand Up @@ -55,10 +56,10 @@ final class SpellingReconcileStrategy(
super.setDocument(document)
}

override def reconcile(region: IRegion): Unit = {
override def reconcile(dirtyRegion: DirtyRegion, subRegion: IRegion): Unit = {
val isSpellingEnabled = store.getBoolean(ESpellingService.PREFERENCE_SPELLING_ENABLED)
if (requestor.isDefined && isSpellingEnabled)
super.reconcile(region)
super.reconcile(dirtyRegion, subRegion)
}

override def getAnnotationModel(): IAnnotationModel =
Expand Down
Expand Up @@ -2,13 +2,13 @@ package org.scalaide.ui.internal.reconciliation

import org.eclipse.core.runtime.IProgressMonitor
import org.eclipse.core.runtime.NullProgressMonitor
import org.scalaide.logging.HasLogger
import org.eclipse.jdt.core.ICompilationUnit
import org.eclipse.jdt.internal.ui.text.java.IJavaReconcilingListener
import org.eclipse.jface.text._
import org.eclipse.jface.text.reconciler.DirtyRegion
import org.eclipse.jface.text.reconciler.IReconcilingStrategy
import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension
import org.eclipse.jface.text.reconciler.DirtyRegion
import org.eclipse.jdt.internal.ui.text.java.IJavaReconcilingListener
import org.eclipse.jdt.core.ICompilationUnit
import org.scalaide.logging.HasLogger
import org.scalaide.ui.editor.InteractiveCompilationUnitEditor
import org.scalaide.util.Utils._

Expand All @@ -35,12 +35,24 @@ class ScalaReconcilingStrategy(icuEditor: InteractiveCompilationUnitEditor) exte
override def setProgressMonitor(pMonitor: IProgressMonitor): Unit = {}

override def reconcile(dirtyRegion: DirtyRegion, subRegion: IRegion): Unit = {
logger.debug("Incremental reconciliation not implemented.")
removeInvalidAnnotations(dirtyRegion)
handleReconciliation()
}

override def reconcile(partition: IRegion): Unit = {
logger.debug("Non incremental reconciliation not implemented.")
}

override def initialReconcile(): Unit = {
// an askReload there adds the scUnit to the list of managed CUs
icUnit.initialReconcile()
handleReconciliation()
}

private def handleReconciliation(): Unit = {
listeningEditor.foreach(_.aboutToBeReconciled())
val errors = icUnit.forceReconcile()
println(errors)

// Some features, such as quick fixes, are dependent upon getting an ICompilationUnit there
val cu: Option[ICompilationUnit] = icUnit.asInstanceOfOpt[ICompilationUnit]
Expand All @@ -53,10 +65,16 @@ class ScalaReconcilingStrategy(icuEditor: InteractiveCompilationUnitEditor) exte
listeningEditor.foreach(_.reconciled(null, false, new NullProgressMonitor()))
}

override def initialReconcile(): Unit = {
// an askReload there adds the scUnit to the list of managed CUs
icUnit.initialReconcile()
reconcile(null)
private def removeInvalidAnnotations(dirtyRegion: DirtyRegion): Unit = {
dirtyRegion.getType match {
case DirtyRegion.INSERT
case DirtyRegion.REMOVE
import org.scalaide.util.eclipse.RegionUtils._
// when Eclipse gains focus, two regions are created. The first one
// removes the entire file content, the second one adds it. We don't
// want to catch this remove region here.
if (!(dirtyRegion.start == 0 && dirtyRegion.length == icUnit.getContents().length))
icuEditor.removeAnnotationsInRegion(dirtyRegion.start, dirtyRegion.end)
}
}

}

0 comments on commit 81fd2c4

Please sign in to comment.