package org.rubypeople.rdt.internal.core.parser; import java.util.List; import org.jruby.ast.BlockNode; import org.jruby.ast.ClassNode; import org.jruby.ast.CommentNode; import org.jruby.ast.DefnNode; import org.jruby.ast.DefsNode; import org.jruby.ast.IterNode; import org.jruby.ast.ModuleNode; import org.jruby.ast.NewlineNode; import org.jruby.ast.Node; import org.jruby.ast.RootNode; import org.jruby.lexer.yacc.ISourcePosition; import org.jruby.parser.RubyParserResult; import org.rubypeople.rdt.internal.ti.util.INodeAcceptor; public class RubyParserWithComments extends RubyParser { @Override protected void postProcessResult(RubyParserResult result) { super.postProcessResult(result); associateCommentsWithNodes(result.getAST(), result.getCommentNodes()); } private void associateCommentsWithNodes(Node ast, List<CommentNode> commentNodes) { for (CommentNode commentNode : commentNodes) { final ISourcePosition pos = commentNode.getPosition(); // Find the closest node that starts or ends on same line Node closestOnSameLine = new ClosestNodeLocator().getClosestNode(ast, pos, new INodeAcceptor() { public boolean doesAccept(Node node) { if (node instanceof NewlineNode || node instanceof RootNode) return false; return (node.getPosition().getEndLine() == pos.getStartLine()) || (node.getPosition().getStartLine() == pos.getStartLine()); } }); if (closestOnSameLine == null) // nothing on same line { // find next type/method def following our position, or any node on very next line Node next = new ClosestNodeLocator().getClosestNode(ast, pos, new INodeAcceptor() { public boolean doesAccept(Node node) { if (node instanceof NewlineNode) return false; return node.getPosition().getStartOffset() > pos.getStartOffset(); } }); if (next != null) { Node surroundingScopeOfComment = getSurroundingScopeNode(ast, pos); Node surroundingScopeOfNext = getSurroundingScopeNode(ast, next.getPosition()); if (surroundingScopeOfComment.equals(surroundingScopeOfNext)) { // comment and next node are siblings, associate to next next.addComment(commentNode); } else { // different scopes, associate to scope surrounding comment surroundingScopeOfComment.addComment(commentNode); } continue; } else { // No next node, associate with current scope Node surroundingScopeOfComment = getSurroundingScopeNode(ast, pos); surroundingScopeOfComment.addComment(commentNode); } } else { // Then "unwrap" out to largest scope on same line Node unwrapped = unwrap(ast, closestOnSameLine, pos.getStartLine()); if (unwrapped != null) unwrapped.addComment(commentNode); } } } private Node getSurroundingScopeNode(Node ast, final ISourcePosition pos) { Node result = new ClosestNodeLocator().getClosestNode(ast, pos, new INodeAcceptor() { public boolean doesAccept(Node node) { if (!(node instanceof ClassNode || node instanceof ModuleNode || node instanceof DefnNode || node instanceof DefsNode || node instanceof IterNode || node instanceof RootNode)) return false; return node.getPosition().getEndLine() > pos.getEndLine() && node.getPosition().getStartLine() < pos.getStartLine(); } }); if (result == null) return ast; return result; } private Node unwrap(Node ast, Node closest, final int line) { while (true) { Node last = closest; closest = new ParentLocator(ast, closest).findParent(new INodeAcceptor() { public boolean doesAccept(Node node) { return !(node instanceof NewlineNode) && !(node instanceof BlockNode) && !(node instanceof RootNode) && (node.getPosition().getEndLine() == line || node.getPosition().getStartLine() == line); } }); if (closest == null) { return last; } } } }