blob: 61bca77e79f417a89e650fbb767bfb72fa8e884d [file] [log] [blame]
package org.eclipse.dltk.ui.text.folding;
import org.eclipse.dltk.core.IModelElement;
import org.eclipse.dltk.core.IModelElementVisitor;
import org.eclipse.dltk.core.ISourceModule;
import org.eclipse.dltk.core.ISourceRange;
import org.eclipse.dltk.core.ISourceReference;
import org.eclipse.dltk.core.ModelException;
import org.eclipse.dltk.internal.core.SourceType;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
public class DefaultElementCommentResolver implements IElementCommentResolver {
private final ISourceModule fModule;
private final String fContent;
private Document fDocument = null;
public DefaultElementCommentResolver(ISourceModule module) {
this(module, null);
}
/**
* @param modelElement
* @param contents
*/
public DefaultElementCommentResolver(ISourceModule module, String contents) {
this.fModule = module;
this.fContent = contents;
}
/**
* Determines the element that contains 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
*/
@Override
public IModelElement getElementByCommentPosition(int offset, int length) {
try {
return getElementByCommentPositionImpl(offset, length);
} catch (BadLocationException e1) {
return null;
} catch (ModelException e) {
return null;
}
}
protected IModelElement getElementByCommentPositionImpl(int offset,
int length) throws BadLocationException, ModelException {
if (fDocument == null) {
fDocument = new Document(getSource());
}
// Determine that the desired position is inside a comment
if (!checkIfPositionIsComment(fDocument, offset))
return null;
// Determine the innermost element that contains the clicked comment
// (for example, class declaration)
IModelElement el = getContainingElement(fModule, 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(fDocument, el);
// Search for first non-comment element after the clicked comment
IModelElement res = searchForNonCommentElement(fDocument, fModule,
offset + length, sourceRangeEnd);
if (res == null)
return el;
return res;
}
/**
* @return
* @throws ModelException
*/
protected String getSource() throws ModelException {
return fContent != null ? fContent : fModule.getSource();
}
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 offset;
private final int length;
public PositionVisitor(int offset, int length) {
this.offset = offset;
this.length = length;
}
@Override
public boolean visit(IModelElement element) {
if (element instanceof ISourceReference) {
final ISourceRange range;
try {
range = ((ISourceReference) element).getSourceRange();
} catch (ModelException e) {
return true;
}
// The comment is entirely inside the element
if (offset >= range.getOffset()
&& offset + length <= range.getOffset()
+ range.getLength()) {
if (element.getElementType() == IModelElement.METHOD
|| element.getElementType() == IModelElement.TYPE)
result = element;
}
}
return true;
}
}
}