package com.redhat.ceylon.eclipse.code.editor; /******************************************************************************* * Copyright (c) 2007 IBM Corporation. * 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: * Robert Fuhrer (rfuhrer@watson.ibm.com) - initial API and implementation *******************************************************************************/ import static com.redhat.ceylon.eclipse.code.editor.CeylonAnnotation.PARSE_ANNOTATION_TYPE; import static com.redhat.ceylon.eclipse.code.editor.CeylonAnnotation.PARSE_ANNOTATION_TYPE_ERROR; import static com.redhat.ceylon.eclipse.code.editor.CeylonAnnotation.PARSE_ANNOTATION_TYPE_INFO; import static com.redhat.ceylon.eclipse.code.editor.CeylonAnnotation.PARSE_ANNOTATION_TYPE_WARNING; import static com.redhat.ceylon.eclipse.code.editor.CeylonAnnotation.isParseAnnotation; import static org.eclipse.core.resources.IMarker.SEVERITY_ERROR; import static org.eclipse.core.resources.IMarker.SEVERITY_INFO; import static org.eclipse.core.resources.IMarker.SEVERITY_WARNING; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.IAnnotationModel; import org.eclipse.jface.text.source.IAnnotationModelExtension; import org.eclipse.ui.texteditor.IDocumentProvider; import com.redhat.ceylon.compiler.typechecker.parser.RecognitionError; import com.redhat.ceylon.compiler.typechecker.tree.Message; import com.redhat.ceylon.eclipse.code.parse.CeylonParseController; import com.redhat.ceylon.eclipse.code.parse.TreeLifecycleListener; import com.redhat.ceylon.eclipse.util.ErrorVisitor; /** * An implementation of the IMessageHandler interface that creates editor annotations * directly from messages. Used for live parsing within a source editor (cf. building, * which uses the class MarkerCreator to create markers). * @author rmfuhrer */ public class AnnotationCreator extends ErrorVisitor implements TreeLifecycleListener { private static class PositionedMessage { public final String message; public final Position pos; public final int code; public final int severity; public final boolean syntaxError; public final int line; public final Message error; private PositionedMessage(Message error, Position pos, int severity) { this.error = error; this.message = error.getMessage(); this.pos = pos; this.code = error.getCode(); this.severity = severity; this.syntaxError = error instanceof RecognitionError; this.line = error.getLine(); } @Override public String toString() { return pos.toString() + " - "+ message; } } private final CeylonEditor editor; private final List<PositionedMessage> messages = new LinkedList<PositionedMessage>(); private final List<Annotation> annotations = new LinkedList<Annotation>(); public AnnotationCreator(CeylonEditor editor) { this.editor = editor; } @Override public void update(CeylonParseController parseController, IProgressMonitor monitor) { if (monitor.isCanceled() || editor.isBackgroundParsingPaused()) { clearMessages(); } else { parseController.getParsedRootNode().visit(this); updateAnnotations(); } } @Override public Stage getStage() { return Stage.TYPE_ANALYSIS; } @Override public void handleMessage(int startOffset, int endOffset, int startCol, int startLine, Message error) { messages.add(new PositionedMessage(error, new Position(startOffset, endOffset-startOffset), getSeverity(error, getWarnForErrors()))); } public void clearMessages() { messages.clear(); } public void updateAnnotations() { IDocumentProvider docProvider = editor.getDocumentProvider(); if (docProvider!=null) { IAnnotationModel model = docProvider.getAnnotationModel(editor.getEditorInput()); if (model instanceof IAnnotationModelExtension) { IAnnotationModelExtension modelExt = (IAnnotationModelExtension) model; Annotation[] oldAnnotations = annotations.toArray(new Annotation[annotations.size()]); Map<Annotation,Position> newAnnotations = new HashMap<Annotation,Position>(messages.size()); for (PositionedMessage pm: messages) { if (!suppressAnnotation(pm)) { Annotation a = createAnnotation(pm); newAnnotations.put(a, pm.pos); annotations.add(a); } } modelExt.replaceAnnotations(oldAnnotations, newAnnotations); } else if (model != null) { // model could be null if, e.g., we're directly browsing a file version in a src repo for (@SuppressWarnings("unchecked") Iterator<Annotation> i = model.getAnnotationIterator(); i.hasNext();) { Annotation a = i.next(); if (isParseAnnotation(a)) { model.removeAnnotation(a); } } for (PositionedMessage pm: messages) { if (!suppressAnnotation(pm)) { Annotation a= createAnnotation(pm); model.addAnnotation(a, pm.pos); annotations.add(a); } } } } messages.clear(); } public boolean suppressAnnotation(PositionedMessage pm) { boolean suppress = false; if (!pm.syntaxError && pm.line>=0) { for (PositionedMessage m: messages) { if (m.syntaxError && m.line==pm.line) { suppress = true; break; } } } return suppress; } private Annotation createAnnotation(PositionedMessage pm) { return new CeylonAnnotation(getAnnotationType(pm), pm.message, pm.code, pm.severity, pm.error); } private String getAnnotationType(PositionedMessage pm) { switch (pm.severity) { case SEVERITY_ERROR: return PARSE_ANNOTATION_TYPE_ERROR; case SEVERITY_WARNING: return PARSE_ANNOTATION_TYPE_WARNING; case SEVERITY_INFO: return PARSE_ANNOTATION_TYPE_INFO; default: return PARSE_ANNOTATION_TYPE; } } }