/******************************************************************************* * Copyright © 2000, 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.HashMap; import org.eclipse.edt.compiler.core.ast.Node; import org.eclipse.edt.ide.core.model.document.IEGLDocument; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextDoubleClickStrategy; import org.eclipse.jface.text.ITextViewer; /** * Double click strategy aware of Java identifier syntax rules. */ public class ScriptDoubleClickSelector implements ITextDoubleClickStrategy { private final static String IF= "if"; private final static String END= "end"; private final static String ELSE= "else"; private final static String WHILE= "while"; private final static String FOR= "for"; private final static String FOREACH= "foreach"; private final static String TRY= "try"; private final static String ONEXCEPTION= "onexception"; private final static String CASE= "case"; private final static String WHEN= "when"; private final static String OTHERWISE= "otherwise"; private final static String OPENUI= "openui"; private final static String ONEVENT= "onevent"; protected ITextViewer fText; protected int fPos; protected int fStartPos; protected int fEndPos; protected static char[] fgBrackets= {'{', '}', '(', ')', '[', ']', '"', '"'}; protected static HashMap fgConditionalPairs = new HashMap(); static { fgConditionalPairs.put(IF, END); fgConditionalPairs.put(IF, ELSE); fgConditionalPairs.put(ELSE, END); fgConditionalPairs.put(WHILE, END); fgConditionalPairs.put(CASE, END); fgConditionalPairs.put(FOR, END); fgConditionalPairs.put(FOREACH, END); fgConditionalPairs.put(TRY, END); fgConditionalPairs.put(TRY, ONEXCEPTION); fgConditionalPairs.put(ONEXCEPTION, END); fgConditionalPairs.put(WHEN, WHEN); fgConditionalPairs.put(WHEN, OTHERWISE); fgConditionalPairs.put(OTHERWISE, END); fgConditionalPairs.put(OPENUI, END); fgConditionalPairs.put(ONEVENT, ONEVENT); fgConditionalPairs.put(ONEVENT, END); } public ScriptDoubleClickSelector() { super(); } /** * @see ITextDoubleClickStrategy#doubleClicked */ public void doubleClicked(ITextViewer text) { fPos= text.getSelectedRange().x; if (fPos < 0) return; fText= text; if (selectBracketBlock()) return; if (selectConditionalBlock()) return; selectWord(); } protected boolean selectConditionalBlock() { if (matchConditional()) { if (fStartPos == fEndPos) fText.setSelectedRange(fStartPos, 0); else fText.setSelectedRange(fStartPos, fEndPos - fStartPos); return true; } return false; } protected boolean matchConditional() { //get the node at offset, //test to see if the offset is at the start of the block for this node //if this node has an "end" (using visitor pattern) IDocument doc= fText.getDocument(); if(doc instanceof IEGLDocument) { IEGLDocument egldoc = (IEGLDocument)doc; Node node = egldoc.getNewModelNodeAtOffset(fPos); if(node != null) //if there is a node { ScriptDoubleClickVisitor visitor = new ScriptDoubleClickVisitor(doc, fPos) ; node.accept(visitor); if(visitor.foundDoubleClickOffset()) { int doubleClickOffset = visitor.getDoubleClickOffset(); fStartPos = Math.min(fPos, doubleClickOffset); fEndPos = Math.max(fPos, doubleClickOffset); return true; } } } return false; /* node = doc.getNodeAtOffset(fPos); GetNodeBodyVisitor visitor = new GetNodeBodyVisitor(fPos); //starting cursor node.accept(getNodeBodyVisitor) */ //if this is at the start point, get the length of the node, minus 3(length of "end") //that's the offset we need to highlight // if this at the start of the block // set start position // search for matching end string (can be multiple) and set end position // else if is this just before the end of the block // set end position // search for matching start string and set start position } protected boolean matchBracketsAt() { char prevChar, nextChar; int i; int bracketIndex1= fgBrackets.length; int bracketIndex2= fgBrackets.length; fStartPos= -1; fEndPos= -1; // get the chars preceding and following the start position try { IDocument doc= fText.getDocument(); prevChar= doc.getChar(fPos - 1); nextChar= doc.getChar(fPos); // is the char either an open or close bracket? for (i= 0; i < fgBrackets.length; i= i + 2) { if (prevChar == fgBrackets[i]) { fStartPos= fPos - 1; bracketIndex1= i; } } for (i= 1; i < fgBrackets.length; i= i + 2) { if (nextChar == fgBrackets[i]) { fEndPos= fPos; bracketIndex2= i; } } if (fStartPos > -1 && bracketIndex1 < bracketIndex2) { fEndPos= searchForClosingBracket(fStartPos, prevChar, fgBrackets[bracketIndex1 + 1], doc); if (fEndPos > -1) return true; else fStartPos= -1; } else if (fEndPos > -1) { fStartPos= searchForOpenBracket(fEndPos, fgBrackets[bracketIndex2 - 1], nextChar, doc); if (fStartPos > -1) return true; else fEndPos= -1; } } catch (BadLocationException x) { } return false; } protected boolean matchWord() { IDocument doc= fText.getDocument(); try { int pos= fPos; char c; while (pos >= 0) { c= doc.getChar(pos); // In EGL '-' is a legal character for a name if (!(Character.isJavaIdentifierPart(c) || c == '-')) break; --pos; } fStartPos= pos; pos= fPos; int length= doc.getLength(); while (pos < length) { c= doc.getChar(pos); // In EGL '-' is a legal character for a name if (!(Character.isJavaIdentifierPart(c) || c == '-')) break; ++pos; } fEndPos= pos; return true; } catch (BadLocationException x) { } return false; } protected int searchForClosingBracket(int startPos, char openBracket, char closeBracket, IDocument doc) throws BadLocationException { int stack= 1; int closePos= startPos + 1; int length= doc.getLength(); char nextChar; while (closePos < length && stack > 0) { nextChar= doc.getChar(closePos); if (nextChar == openBracket && nextChar != closeBracket) stack++; else if (nextChar == closeBracket) stack--; closePos++; } if (stack == 0) return closePos - 1; else return -1; } protected int searchForOpenBracket(int startPos, char openBracket, char closeBracket, IDocument doc) throws BadLocationException { int stack= 1; int openPos= startPos - 1; char nextChar; while (openPos >= 0 && stack > 0) { nextChar= doc.getChar(openPos); if (nextChar == closeBracket && nextChar != openBracket) stack++; else if (nextChar == openBracket) stack--; openPos--; } if (stack == 0) return openPos + 1; else return -1; } protected boolean selectBracketBlock() { if (matchBracketsAt()) { if (fStartPos == fEndPos) fText.setSelectedRange(fStartPos, 0); else fText.setSelectedRange(fStartPos + 1, fEndPos - fStartPos - 1); return true; } return false; } protected void selectWord() { if (matchWord()) { if (fStartPos == fEndPos) fText.setSelectedRange(fStartPos, 0); else fText.setSelectedRange(fStartPos + 1, fEndPos - fStartPos - 1); } } }