package org.jactr.eclipse.ui.editor.highlighter; /* * default logging */ import java.net.URL; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.antlr.runtime.tree.CommonTree; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.BadPositionCategoryException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.IAnnotationModelExtension; import org.eclipse.ui.progress.UIJob; import org.jactr.eclipse.core.ast.Support; import org.jactr.eclipse.core.comp.ICompilationUnit; import org.jactr.eclipse.ui.editor.ACTRModelEditor; import org.jactr.eclipse.ui.editor.markers.ASTPosition; import org.jactr.eclipse.ui.editor.markers.PositionMarker; import org.jactr.io.antlr3.builder.JACTRBuilder; import org.jactr.io.antlr3.misc.ASTSupport; public class ReferenceHighlighter { /** * Logger definition */ static private final transient Log LOGGER = LogFactory .getLog(ReferenceHighlighter.class); static private final String HIGHLIGHT_POSITION_ID = "org.jactr.highlight.positions"; private ACTRModelEditor _editor; private int _lastOffset = 0; private CommonTree _lastNode; private Annotation[] _lastAnnotations = new Annotation[0]; private UIJob _job; private volatile int _caretOffset; public ReferenceHighlighter(ACTRModelEditor editor) { _editor = editor; } public void highlight(int caretOffset) { if (_lastOffset == caretOffset) return; if (_job != null) _job.cancel(); _caretOffset = caretOffset; if (_job == null) { _job = new UIJob("Highlighting references") { @Override public IStatus runInUIThread(IProgressMonitor monitor) { try { highlightInternal(_caretOffset); } catch (Exception e) { LOGGER .error("Failed to highlight references at " + _caretOffset, e); } return Status.OK_STATUS; } }; _job.setSystem(true); } _job.schedule(350); } private ASTPosition getContainer(IDocument document, URL base, int caretOffset) { /* * this will be production,chunk,chunktype,or buffer.. and I can inspect the * children more closely */ ASTPosition container = PositionMarker.getPosition(document, base, caretOffset); while (container != null && container.getNode().getType() != JACTRBuilder.PRODUCTION && container.getNode().getType() != JACTRBuilder.CHUNK && container.getNode().getType() != JACTRBuilder.CHUNK_TYPE) container = container.getParent(); if (container == null) return null; if (LOGGER.isDebugEnabled()) LOGGER.debug("ASTPosition @ " + caretOffset + " : " + container); return container; } protected void removeAnnotations(IDocument document) { /* * clear out the old positions */ try { document.removePositionCategory(HIGHLIGHT_POSITION_ID); } catch (BadPositionCategoryException e1) { } try { ((IAnnotationModelExtension) _editor.getAnnotationModel()) .replaceAnnotations(_lastAnnotations, Collections.EMPTY_MAP); } catch (Exception e) { // there may not be an annotation model } _lastAnnotations = new Annotation[0]; } protected void addAnnotations(IDocument document, Map<Annotation, Position> newAnnotations) { if (newAnnotations.size() != 0) { document.addPositionCategory(HIGHLIGHT_POSITION_ID); if (LOGGER.isDebugEnabled()) LOGGER.debug("Adding positions"); /* * add the new */ for (Position position : newAnnotations.values()) try { document.addPosition(HIGHLIGHT_POSITION_ID, position); } catch (BadLocationException e) { LOGGER .error( "ReferenceHighlighter.highlight threw BadLocationException : ", e); } catch (BadPositionCategoryException e) { LOGGER .error( "ReferenceHighlighter.highlight threw BadPositionCategoryException : ", e); } if (LOGGER.isDebugEnabled()) LOGGER.debug("Adding annotations"); ((IAnnotationModelExtension) _editor.getAnnotationModel()) // .replaceAnnotations(_lastAnnotations, newAnnotations); .replaceAnnotations(new Annotation[0], newAnnotations); _lastAnnotations = newAnnotations.keySet().toArray(_lastAnnotations); } } protected void highlightInternal(int caretOffset) { /* * we use base to constrain our search to ASTs that are directly contained * in this file.. */ URL base = _editor.getBase(); IDocument document = _editor.getDocumentProvider().getDocument( _editor.getEditorInput()); if (document == null) return; ASTPosition container = getContainer(document, base, caretOffset); if (container == null) { removeAnnotations(document); return; } CommonTree containerNode = container.getNode(); CommonTree actualNode = Support.getNodeOfOffset(containerNode, caretOffset, base); if (LOGGER.isDebugEnabled()) LOGGER.debug("Actual node : " + actualNode + "." + (actualNode != null ? actualNode.getType() : -1)); if (_lastNode == actualNode) return; Map<Annotation, Position> newAnnotations = highlightReferences( containerNode, actualNode); removeAnnotations(document); addAnnotations(document, newAnnotations); _lastNode = actualNode; _lastOffset = caretOffset; } private Map<Annotation, Position> highlightReferences( CommonTree containerNode, CommonTree actualNode) { Map<Annotation, Position> newAnnotations = Collections.emptyMap(); if (actualNode == null) return newAnnotations; switch (actualNode.getType()) { case JACTRBuilder.VARIABLE: newAnnotations = highlightVariables(containerNode, actualNode.getText()); break; case JACTRBuilder.IDENTIFIER: case JACTRBuilder.CHUNK_IDENTIFIER: newAnnotations = highlightChunks(_editor.getCompilationUnit(), actualNode.getText()); break; case JACTRBuilder.PARENT: case JACTRBuilder.CHUNK_TYPE_IDENTIFIER: newAnnotations = highlightChunkTypes(_editor.getCompilationUnit(), actualNode.getText()); break; case JACTRBuilder.NAME: switch (containerNode.getType()) { case JACTRBuilder.CHUNK: newAnnotations = highlightChunks(_editor.getCompilationUnit(), actualNode.getText()); break; case JACTRBuilder.CHUNK_TYPE: newAnnotations = highlightChunkTypes(_editor.getCompilationUnit(), actualNode.getText()); break; } break; } return newAnnotations; } private Map<Annotation, Position> highlightVariables(CommonTree production, String variableName) { /* * find all the variable elements */ HashMap<Annotation, Position> rtn = new HashMap<Annotation, Position>(); Collection<CommonTree> variables = ASTSupport.getAllDescendantsWithType( production, JACTRBuilder.VARIABLE); for (CommonTree variable : variables) { String tmp = variable.getText(); if (variableName.equalsIgnoreCase(tmp)) { Region span = PositionMarker.getNodeSpan(variable, null); if (LOGGER.isDebugEnabled()) LOGGER.debug("variable @ " + span); Position position = new Position(span.getOffset(), span.getLength()); Annotation annotation = new Annotation( HighlightAnnotations.VARIABLE_ID, false, variableName); rtn.put(annotation, position); } else if (LOGGER.isDebugEnabled()) LOGGER.debug(tmp + " doesn't match " + variableName); } /* * ideally we also want to search out any strings and see if they contain * the variablename */ return rtn; } private Map<Annotation, Position> highlightChunks( ICompilationUnit modelDescriptor, String chunkName) { Map<Annotation, Position> map = highlightNamable(modelDescriptor, chunkName, JACTRBuilder.CHUNK, JACTRBuilder.CHUNK_IDENTIFIER, HighlightAnnotations.CHUNK_ID, _editor.getBase()); map.putAll(highlightNamable(modelDescriptor, chunkName, -1, JACTRBuilder.IDENTIFIER, HighlightAnnotations.CHUNK_ID, _editor.getBase())); return map; } private Map<Annotation, Position> highlightChunkTypes( ICompilationUnit modelDescriptor, String chunkTypeName) { Map<Annotation, Position> map = highlightNamable(modelDescriptor, chunkTypeName, JACTRBuilder.CHUNK_TYPE, JACTRBuilder.CHUNK_TYPE_IDENTIFIER, HighlightAnnotations.CHUNK_TYPE_ID, _editor.getBase()); map.putAll(highlightNamable(modelDescriptor, chunkTypeName, -1, JACTRBuilder.PARENT, HighlightAnnotations.CHUNK_TYPE_ID, _editor.getBase())); return map; } private Map<Annotation, Position> highlightNamable( ICompilationUnit modelDescriptor, String name, int baseType, int identifierType, String annotationType, URL source) { HashMap<Annotation, Position> rtn = new HashMap<Annotation, Position>(); if (baseType >= 0) { // get all the chunks so we can mark the actual instance of the chunk.. Map<String, CommonTree> allOfBaseType = modelDescriptor .getNamedContents(baseType); CommonTree definition = allOfBaseType.get(name); if (definition != null) { definition = ASTSupport.getFirstDescendantWithType(definition, JACTRBuilder.NAME); Region span = PositionMarker.getNodeSpan(definition, source); if (span != null) rtn.put(new Annotation(annotationType, false, name), new Position( span.getOffset(), span.getLength())); } } // now we get all chunk_id so that we can mark all references to chunk.. for (CommonTree identifier : modelDescriptor.getContents(identifierType)) if (name.equalsIgnoreCase(identifier.getText())) { Region span = PositionMarker.getNodeSpan(identifier, source); if (span != null) rtn.put(new Annotation(annotationType, false, name), new Position( span.getOffset(), span.getLength())); } return rtn; } }