/******************************************************************************* * Copyright © 2008, 2013 IBM Corporation and others. * 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 * * Contributors: * IBM Corporation - initial API and implementation * *******************************************************************************/ package org.eclipse.edt.ide.ui.internal.editor; import java.util.List; import org.eclipse.edt.compiler.core.IEGLConstants; import org.eclipse.edt.compiler.core.ast.AbstractASTVisitor; import org.eclipse.edt.compiler.core.ast.CaseStatement; import org.eclipse.edt.compiler.core.ast.ElseBlock; import org.eclipse.edt.compiler.core.ast.Enumeration; import org.eclipse.edt.compiler.core.ast.Expression; import org.eclipse.edt.compiler.core.ast.ExternalType; import org.eclipse.edt.compiler.core.ast.ForEachStatement; import org.eclipse.edt.compiler.core.ast.ForStatement; import org.eclipse.edt.compiler.core.ast.Handler; import org.eclipse.edt.compiler.core.ast.IfStatement; import org.eclipse.edt.compiler.core.ast.Interface; import org.eclipse.edt.compiler.core.ast.Library; import org.eclipse.edt.compiler.core.ast.Name; import org.eclipse.edt.compiler.core.ast.NestedFunction; import org.eclipse.edt.compiler.core.ast.Node; import org.eclipse.edt.compiler.core.ast.OnExceptionBlock; import org.eclipse.edt.compiler.core.ast.Part; import org.eclipse.edt.compiler.core.ast.Program; import org.eclipse.edt.compiler.core.ast.Record; import org.eclipse.edt.compiler.core.ast.Service; import org.eclipse.edt.compiler.core.ast.TryStatement; import org.eclipse.edt.compiler.core.ast.Type; import org.eclipse.edt.compiler.core.ast.WhileStatement; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; public class ScriptDoubleClickVisitor extends AbstractASTVisitor { private IDocument fDoc; //the selection cursor's position.x private int cursorOffset = -1; //staring and ending offset of a node // private int startingPos = -1; // private int endPos = -1; //what's the select offset if to highlight the body of the part private int doubleClickSelOffset = -1; private boolean foundDoubleClickOffset = false; public ScriptDoubleClickVisitor(IDocument doc, int cusorPosition) { fDoc = doc; cursorOffset = cusorPosition; } public boolean foundDoubleClickOffset() { return foundDoubleClickOffset; } public int getDoubleClickOffset() { return doubleClickSelOffset; } private void calculateStartingEndingPosition(Node startingPositionNode, Node currentNode) { int blockStartingPos = startingPositionNode.getOffset() + startingPositionNode.getLength(); calculateStartingEndingPosition(blockStartingPos, currentNode); } private void calculateStartingEndingPosition(int blockStartingPosition, Node currentNode) { int blockEndPos = currentNode.getOffset() + currentNode.getLength() - IEGLConstants.KEYWORD_END.length(); calculateStartingEndingPosition(blockStartingPosition, blockEndPos); } private void calculateStartingEndingPosition(int blockStartingPosition, int blockEndingPosition) { if(cursorOffset == blockStartingPosition) { foundDoubleClickOffset = true; doubleClickSelOffset = blockEndingPosition; } else if(cursorOffset == blockEndingPosition) { foundDoubleClickOffset = true; doubleClickSelOffset = blockStartingPosition; } else foundDoubleClickOffset = false; } public boolean visit(Record record) { calcaulatePartPosition(record); return false; } public boolean visit(Enumeration enumeration) { calcaulatePartPosition(enumeration); return false; }; public boolean visit(ExternalType externalType) { Name startingPositionNode = null; if(externalType.hasSubType()) startingPositionNode = externalType.getSubType(); else if(!externalType.getExtendedTypes().isEmpty()) startingPositionNode = (Name) externalType.getExtendedTypes().get(0); else startingPositionNode = externalType.getName(); calculateStartingEndingPosition(startingPositionNode, externalType); return false; }; public boolean visit(Handler handler) { calcaulatePartPosition(handler); return false; } private int searchForClosingBracket(int startPos, char closeBracket, IDocument doc) { int closePos = startPos; int length = doc.getLength(); char nextChar; boolean bFnd = false; try { while(closePos < length && !bFnd) { nextChar = doc.getChar(closePos); if(nextChar == closeBracket) bFnd = true; closePos ++; } } catch (BadLocationException e) { closePos = -1; e.printStackTrace(); } if(bFnd) return closePos; else return -1; } public boolean visit(Program program) { calcaulatePartPosition(program); return false; } public boolean visit(Library library) { calcaulatePartPosition(library); return false; } private void calcaulatePartPosition(Part part) { Name startingPositionNode = null; if(part.hasSubType()) startingPositionNode = part.getSubType(); else startingPositionNode = part.getName(); calculateStartingEndingPosition(startingPositionNode, part); } public boolean visit(NestedFunction nestedFunction) { Node startingNode = null; if(nestedFunction.hasReturnType()) { startingNode = nestedFunction.getReturnType(); //try to get the closing paren ') for the return type } else { //try to get the closing paren ')' for the function parameters startingNode = nestedFunction.getName(); } int startingNodePos = startingNode.getOffset() + startingNode.getLength(); int closingParenPos = searchForClosingBracket(startingNodePos, ')', fDoc); calculateStartingEndingPosition(closingParenPos, nestedFunction); return false; } public boolean visit(Service service) { Name startingPositionNode = null; List implementList = service.getImplementedInterfaces(); if(implementList != null && !implementList.isEmpty()) { //get the last implements name int size = implementList.size(); startingPositionNode = (Name)(implementList.get(size-1)); } else startingPositionNode = service.getName(); calculateStartingEndingPosition(startingPositionNode, service); return false; } public boolean visit(Interface interfaceNode) { calcaulatePartPosition(interfaceNode); return false; } public boolean visit(TryStatement tryStatement) { //starting position is the node offset + 3(length of the "try" keyword) int startingPosition = tryStatement.getOffset() + IEGLConstants.KEYWORD_TRY.length(); List exceptionBlocks = tryStatement.getOnExceptionBlocks(); //you may have 0 or * onExceptionBlock return visitParentWithChildrenBlock(startingPosition, tryStatement, exceptionBlocks); } private boolean visitParentWithChildrenBlock(int startingPosition, Node parentNode, List childBlocks) { if(childBlocks != null && !childBlocks.isEmpty()) { int parentNodeEndPos = parentNode.getOffset() + parentNode.getLength(); //if the cursor is right before the keywards "END", and the parent has child block if(cursorOffset == parentNodeEndPos-IEGLConstants.KEYWORD_END.length()) { int size = childBlocks.size(); Node lastChildBlock = (Node)(childBlocks.get(size-1)); //we need to treat this node as onException block lastChildBlock.accept(this); } else { //it ends before the keyward "onException" block Node firstChildBlock = (Node)(childBlocks.get(0)); //the block ends before the onException block int blockEndPos = firstChildBlock.getOffset(); calculateStartingEndingPosition(startingPosition, blockEndPos); } } else { //use end calculateStartingEndingPosition(startingPosition, parentNode); } return false; } private int getOnExceptionBlockStartingPosition(OnExceptionBlock onExceptionBlock) { int onExceptionBlockStartingPos = onExceptionBlock.getOffset() + IEGLConstants.KEYWORD_ONEXCEPTION.length(); Type exceptionType = onExceptionBlock.getExceptionType(); int exceptionTypePos = exceptionType.getOffset() + exceptionType.getLength(); onExceptionBlockStartingPos = searchForClosingBracket(exceptionTypePos, ')', fDoc); return onExceptionBlockStartingPos; } public boolean visit(OnExceptionBlock onExceptionBlock) { Node parentNode = onExceptionBlock.getParent(); if(parentNode instanceof TryStatement) { TryStatement tryStatement = (TryStatement)parentNode; List exceptionBlocks = tryStatement.getOnExceptionBlocks(); int startingPosition = getOnExceptionBlockStartingPosition(onExceptionBlock); visitListChildrenBlock(onExceptionBlock, parentNode, exceptionBlocks, startingPosition); } return false; } public boolean visit(CaseStatement caseStatement) { if(caseStatement.hasCriterion()) { Expression expr = caseStatement.getCriterion(); int exprEndPos = expr.getOffset() + expr.getLength(); //no need to find the closing paren, the case criterion expression length included the parenthesis. //int closingParenPos = searchForClosingBracket(exprEndPos, ')', fDoc); calculateStartingEndingPosition(exprEndPos, caseStatement); } else { int startingPosition = caseStatement.getOffset() + IEGLConstants.KEYWORD_CASE.length(); calculateStartingEndingPosition(startingPosition, caseStatement); } return false; } public boolean visit(IfStatement ifStatement) { Expression expr = ifStatement.getCondition(); int exprEndPos = expr.getOffset() + expr.getLength(); int blockStartingPos = searchForClosingBracket(exprEndPos, ')', fDoc); if(ifStatement.hasElse()) { //it ends before the else block ElseBlock elseNode = ifStatement.getElse(); int ifStatementEndPos = ifStatement.getOffset() + ifStatement.getLength(); //if the cursor is right before the keyword "END", and the if statement has else block if(cursorOffset == ifStatementEndPos-IEGLConstants.KEYWORD_END.length()) { //we need to treat this node as elseBlock elseNode.accept(this); } else { int blockEndingPos = elseNode.getOffset(); calculateStartingEndingPosition(blockStartingPos, blockEndingPos); } } else { //it ends before "end" calculateStartingEndingPosition(blockStartingPos, ifStatement); } return false; } public boolean visit(ElseBlock elseBlock) { return visitSecondHalfChildBlock(elseBlock, IEGLConstants.KEYWORD_ELSE.length()); } private boolean visitSecondHalfChildBlock(Node secondHalfNode, int keywordLength) { Node parentNode = secondHalfNode.getParent(); if(cursorOffset == secondHalfNode.getOffset()) { //cursor is at the beginning of the 2nd half node(else, onException..), //so we need to treat it as the end of its parent parentNode.accept(this); } else { int startingPosition = secondHalfNode.getOffset() + keywordLength; calculateStartingEndingPosition(startingPosition, parentNode); } return false; } public boolean visit(WhileStatement whileStatement) { Expression expr = whileStatement.getExpr(); int exprEndPos = expr.getOffset() + expr.getLength(); int blockStartingPos = searchForClosingBracket(exprEndPos, ')', fDoc); calculateStartingEndingPosition(blockStartingPos, whileStatement); return false; } public boolean visit(ForStatement forStatement) { int startSearchRParen = forStatement.getOffset(); Expression stepExpr = forStatement.getDeltaExpression(); if(stepExpr != null) { startSearchRParen = stepExpr.getOffset() + stepExpr.getLength(); } else { //no explicit step expression Expression toExpr = forStatement.getEndIndex(); startSearchRParen = toExpr.getOffset() + toExpr.getLength(); } int blockStartingPos = searchForClosingBracket(startSearchRParen, ')', fDoc); calculateStartingEndingPosition(blockStartingPos, forStatement); return false; } public boolean visit(ForEachStatement forEachStatement) { int startSearchRParen = forEachStatement.getClosingParenOffset(); int blockStartingPos = searchForClosingBracket(startSearchRParen, ')', fDoc); calculateStartingEndingPosition(blockStartingPos, forEachStatement); return false; } private void visitListChildrenBlock(Node childBlock, Node parentNode, List childBlocks, int startingPosition) { int size = childBlocks.size(); boolean bFnd = false; int index=-1; for(int i=0; i<size && !bFnd; i++) { Node childBlockElement = (Node)(childBlocks.get(i)); if(childBlockElement.getOffset() == childBlock.getOffset()) { bFnd = true; //we found the node that's passed in index = i; } } if(bFnd) { //int startingPosition = onEventBlock.getOffset() + IEGLConstants.KEYWORD_ONEVENT.length(); if(index==0 && index==size-1) //only 1 onEvent block { if(cursorOffset == childBlock.getOffset()) parentNode.accept(this); else calculateStartingEndingPosition(startingPosition, parentNode); } else if(index == size-1) //last onEvent and more than 1 onEvent { if(cursorOffset == childBlock.getOffset()) { Node previousEventBlock = (Node)(childBlocks.get(index-1)); previousEventBlock.accept(this); } else calculateStartingEndingPosition(startingPosition, parentNode); } else { Node nextChildBlock = (Node)(childBlocks.get(index+1)); int endingPosition = nextChildBlock.getOffset(); if(index==0) //1st onEvent and more than 1 onEvent { if(cursorOffset == childBlock.getOffset()) parentNode.accept(this); else calculateStartingEndingPosition(startingPosition, endingPosition); } else //in the middle { if(cursorOffset == childBlock.getOffset()) { Node previousChildBlock = (Node)(childBlocks.get(index-1)); previousChildBlock.accept(this); } else calculateStartingEndingPosition(startingPosition, endingPosition); } } } } }