package com.redhat.ceylon.eclipse.code.editor; import static com.redhat.ceylon.eclipse.code.editor.CeylonTaskUtil.addTaskAnnotation; import static com.redhat.ceylon.eclipse.code.parse.TreeLifecycleListener.Stage.TYPE_ANALYSIS; import static com.redhat.ceylon.eclipse.ui.CeylonPlugin.PLUGIN_ID; import static com.redhat.ceylon.eclipse.util.Nodes.getIdentifyingNode; import java.util.Iterator; import java.util.List; import org.antlr.runtime.CommonToken; 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.ui.IEditorInput; import com.redhat.ceylon.compiler.typechecker.parser.CeylonLexer; import com.redhat.ceylon.compiler.typechecker.tree.Node; import com.redhat.ceylon.compiler.typechecker.tree.Tree; import com.redhat.ceylon.compiler.typechecker.tree.Visitor; import com.redhat.ceylon.eclipse.code.parse.CeylonParseController; import com.redhat.ceylon.eclipse.code.parse.TreeLifecycleListener; import com.redhat.ceylon.ide.common.util.types_; import com.redhat.ceylon.model.typechecker.model.Declaration; import com.redhat.ceylon.model.typechecker.model.Unit; /** * Responsible for adding refinement annotations to the * vertical ruler, and updating the highlight range in * the vertical ruler. * */ public class RefinementAnnotationCreator implements TreeLifecycleListener { public static final String TODO_ANNOTATION_TYPE = PLUGIN_ID + ".todo"; private CeylonEditor editor; public RefinementAnnotationCreator(CeylonEditor editor) { this.editor = editor; } @Override public Stage getStage() { return TYPE_ANALYSIS; } @Override public void update( CeylonParseController parseController, IProgressMonitor monitor) { if (editor.isBackgroundParsingPaused() || monitor.isCanceled()) { return; } final CeylonParseController cpc = parseController; if (cpc.getStage().ordinal() >= getStage().ordinal()) { final Tree.CompilationUnit rootNode = cpc.getLastCompilationUnit(); List<CommonToken> tokens = cpc.getTokens(); if (rootNode == null) { return; } IEditorInput editorInput = editor.getEditorInput(); final IAnnotationModel model = editor.getDocumentProvider() .getAnnotationModel(editorInput); if (model==null) { return; } for (@SuppressWarnings("unchecked") Iterator<Annotation> iter = model.getAnnotationIterator(); iter.hasNext();) { Annotation a = iter.next(); if (a instanceof RefinementAnnotation || a.getType().equals(TODO_ANNOTATION_TYPE)) { model.removeAnnotation(a); } } new Visitor() { @Override public void visit(Tree.Declaration that) { super.visit(that); Declaration dec = that.getDeclarationModel(); if (dec!=null) { if (dec.isActual()) { addRefinementAnnotation( model, that, that.getIdentifier(), dec); } } } @Override public void visit(Tree.SpecifierStatement that) { super.visit(that); if (that.getRefinement()) { Declaration dec = that.getDeclaration(); if (dec!=null) { if (dec.isActual()) { addRefinementAnnotation( model, that, that.getBaseMemberExpression(), dec); } } } } }.visit(rootNode); for (CommonToken token : tokens) { int type = token.getType(); if (type == CeylonLexer.LINE_COMMENT || type == CeylonLexer.MULTI_COMMENT) { addTaskAnnotation(token, model); } } } } private void addRefinementAnnotation( IAnnotationModel model, Tree.StatementOrArgument that, Node node, Declaration dec) { Declaration refined = types_.get_() .getRefinedDeclaration(dec); if (refined!=null) { Declaration container = (Declaration) refined.getContainer(); Unit unit = that.getUnit(); String description = "refines " + container.getName(unit) + "." + refined.getName(unit); int line = node.getToken().getLine(); RefinementAnnotation ra = new RefinementAnnotation(description, refined, line); Node identifyingNode = getIdentifyingNode(that); model.addAnnotation(ra, new Position( identifyingNode.getStartIndex(), identifyingNode.getDistance())); } } }