/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.api.editor.annotation;
import org.eclipse.che.ide.api.editor.text.Position;
import org.eclipse.che.ide.api.editor.text.annotation.Annotation;
import org.eclipse.che.ide.api.editor.document.Document;
import org.eclipse.che.ide.api.editor.minimap.Minimap;
import org.eclipse.che.ide.api.editor.text.LinearRange;
import org.eclipse.che.ide.api.editor.text.TextPosition;
import org.eclipse.che.ide.util.loging.Log;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
/**
* Renderer for annotation marks in the minimap (on the right margin of the text).
*/
public class MinimapAnnotationRenderer implements AnnotationModelHandler, ClearAnnotationModelHandler {
/** The logger. */
private static Logger LOG = Logger.getLogger(MinimapAnnotationRenderer.class.getName());
/**
* The component responsible for minimap handling.
*/
private Minimap minimap;
/**
* The document.
*/
private Document document;
/**
* Sets the component responsible for minimap handling.
* @param minimap the component
*/
public void setMinimap(final Minimap minimap) {
this.minimap = minimap;
}
@Override
public void onAnnotationModel(final AnnotationModelEvent event) {
// annotation is mutable, not easy to use a set
final Map<Integer, List<Annotation>> toRestore = new HashMap<>();
// remove removed and changed annotations
for (final Annotation annotation : event.getRemovedAnnotations()) {
LOG.fine("Remove annotation: " + annotation);
removeAnnotationItem(event, annotation, toRestore);
}
for (final Annotation annotation : event.getChangedAnnotations()) {
LOG.fine("Remove changed annotation: " + annotation);
removeAnnotationItem(event, annotation, toRestore);
}
final Map<String, String> decorations = event.getAnnotationModel().getAnnotationStyle();
// restore annotations that were deleted but shouldn't have
for (final List<Annotation> annotations : toRestore.values()) {
for (final Annotation annotation: annotations) {
addAnnotationItem(event.getAnnotationModel(), annotation, decorations);
}
}
// add new and changed (new version) annotation
for (final Annotation annotation : event.getAddedAnnotations()) {
LOG.fine("Add annotation: " + annotation);
addAnnotationItem(event.getAnnotationModel(), annotation, decorations);
}
for (final Annotation annotation : event.getChangedAnnotations()) {
LOG.fine("Add back changed annotation: " + annotation);
addAnnotationItem(event.getAnnotationModel(), annotation, decorations);
}
}
private void removeAnnotationItem(final AnnotationModelEvent event, final Annotation annotation,
final Map<Integer, List<Annotation>> toRestore) {
final Position position = event.getPositionOfRemovedAnnotation(annotation);
final TextPosition textPosition = this.document.getPositionFromIndex(position.getOffset());
final int line = textPosition.getLine();
// remove all marks on the line
this.minimap.removeMarks(line, line);
// restore marks that are not removed
final LinearRange rangeForLine = this.document.getLinearRangeForLine(line);
final AnnotationModel model = event.getAnnotationModel();
final Iterator<Annotation> it = model.getAnnotationIterator(rangeForLine.getStartOffset(),
rangeForLine.getLength(), false, true);
while (it.hasNext()) {
final Annotation current = it.next();
List<Annotation> lineAnnotations = toRestore.get(line);
if (!current.equals(annotation)) {
if (lineAnnotations == null) {
lineAnnotations = new ArrayList<>();
toRestore.put(line, lineAnnotations);
}
lineAnnotations.add(current);
} else {
if (lineAnnotations != null) {
lineAnnotations.removeAll(Collections.singletonList(current));
if (lineAnnotations.isEmpty()) {
toRestore.remove(line);
}
}
}
}
}
private void addAnnotationItem(final AnnotationModel model, final Annotation annotation, final Map<String, String> decorations) {
final Position position = model.getPosition(annotation);
if (position == null) {
Log.warn(MinimapAnnotationRenderer.class, "No position for annotation " + annotation);
return;
}
// final TextPosition textPosition = this.document.getPositionFromIndex(position.getOffset());
// final int line = textPosition.getLine();
final String style = decorations.get(annotation.getType());
this.minimap.addMark(position.getOffset(), style, annotation.getLayer(), annotation.getText());
}
@Override
public void onClearModel(final ClearAnnotationModelEvent event) {
this.minimap.clearMarks();
}
public void setDocument(final Document document) {
this.document = document;
}
}