/******************************************************************************* * Copyright (c) 2005, 2012 eBay Inc. * 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 * *******************************************************************************/ package org.eclipse.vjet.eclipse.internal.ui.text.folding; import org.eclipse.dltk.mod.core.IField; import org.eclipse.dltk.mod.core.IMethod; import org.eclipse.dltk.mod.core.IModelElement; import org.eclipse.dltk.mod.core.IModelElementVisitor; import org.eclipse.dltk.mod.core.ISourceModule; import org.eclipse.dltk.mod.core.ISourceRange; import org.eclipse.dltk.mod.core.IType; import org.eclipse.dltk.mod.core.ModelException; import org.eclipse.dltk.mod.internal.core.SourceRefElement; import org.eclipse.dltk.mod.internal.core.SourceType; import org.eclipse.dltk.mod.internal.core.VjoSourceModule; import org.eclipse.dltk.mod.ui.text.folding.IElementCommentResolver; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.Document; public class VjetElementCommentResolver implements IElementCommentResolver { /** * Determines the element that related with the clicked comment * * @throws ModelException */ protected IModelElement getContainingElement(IModelElement el, int offset, int length) throws ModelException { final PositionVisitor visitor = new PositionVisitor(offset, length); el.accept(visitor); return visitor.result; } /** * Returns the model element that the comment corresponds to */ public IModelElement getElementByCommentPosition(ISourceModule content, int offset, int length) { try { return getElementByCommentPositionImpl(content, offset, length); } catch (BadLocationException e1) { return null; } catch (ModelException e) { return null; } } protected IModelElement getElementByCommentPositionImpl(ISourceModule content, int offset, int length) throws BadLocationException, ModelException { Document d = new Document(content.getSource()); // Determine that the desired position is inside a comment // if (!checkIfPositionIsComment(d, offset)) // return null; // Determine the innermost element that contains the clicked comment // (for example, class declaration) IModelElement el = getContainingElement(content, offset, length); // If the comment is inside a method, we do not need to process further if (el != null && el.getElementType() == IModelElement.METHOD) return el; // Determine the position after which the search will be stopped - for // example, EOF or end of the class declaration int sourceRangeEnd = getSourceRangeEnd(d, el); // Search for first non-comment element after the clicked comment IModelElement res = searchForNonCommentElement(d, content, offset + length, sourceRangeEnd); if (res == null) return el; return res; } protected int getSourceRangeEnd(Document d, IModelElement el) throws ModelException { int sourceRangeEnd = d.getLength(); // If the comment is inside a class, we need to stop searching the // element once we leave the class boundaries if (el != null && el.getElementType() == IModelElement.TYPE) { SourceType t = (SourceType) el; sourceRangeEnd = t.getSourceRange().getOffset() + t.getSourceRange().getLength(); } return sourceRangeEnd; } // protected boolean checkIfPositionIsComment(Document d, int offset) throws BadLocationException { // int line = d.getLineOfOffset(offset); // int q = d.getLineOffset(line); // // while (q < d.getLength() && Character.isWhitespace(d.getChar(q)) && q <= offset) { // q++; // } // // if (d.getChar(q) != '#') { // /* First non-space char is not a comment start, so stop processing */ // return false; // } // // return true; // } protected IModelElement searchForNonCommentElement(Document d, ISourceModule content, int endOfCommentOffset, int lowerbound) throws BadLocationException, ModelException { IModelElement res = null; int off = endOfCommentOffset; int line = d.getLineOfOffset(off); off = d.getLineOffset(line); while (off < lowerbound) { while (off < lowerbound - 1 && Character.isWhitespace(d.getChar(off))) { off++; } if (d.getChar(off) != '#') { // It's neither a comment nor whitespace, so we can get the // model element at this position res = content.getElementAt(off); break; } line++; off = d.getLineOffset(line); } return res; } /** * Visitor to search the AST for elements that contain the clicked comment */ private static class PositionVisitor implements IModelElementVisitor { IModelElement result = null; private final int commentOffset; private final int commentLength; private int lowestOffset = -1; public PositionVisitor(int offset, int length) { this.commentOffset = offset; this.commentLength = length; } public boolean visit(IModelElement el) { //only the below 4 model Element can have javadoc if (el instanceof IType || el instanceof IMethod || el instanceof ISourceModule || el instanceof IField) { ISourceRange range = null; try { if (el instanceof VjoSourceModule) { range = ((VjoSourceModule) el).getSourceRange(); } if (el instanceof SourceRefElement) { // The comment is entirely inside the element range = ((SourceRefElement) el).getSourceRange(); } if (range != null) { //is containing by current method or type element if (commentOffset >= range.getOffset() && commentOffset + commentLength <= range.getOffset() + range.getLength()) { if (el.getElementType() == IModelElement.METHOD) { //in a method, is not javadoc? result = el; return false; } } if (commentOffset + commentLength <= range.getOffset()) { int offsetDistance = range.getOffset() - commentOffset; if (result == null || offsetDistance < lowestOffset) { if (el instanceof VjoSourceModule) { result = ((VjoSourceModule) el).getTypes()[0]; lowestOffset = offsetDistance; } if (el.getElementType() == IModelElement.METHOD || el.getElementType() == IModelElement.TYPE || el.getElementType() == IModelElement.FIELD) { result = el; lowestOffset = offsetDistance; } } } } } catch (ModelException e) { e.printStackTrace(); } return true; } return false; } } }