/*******************************************************************************
* Copyright (c) 2008 Scott Stanchfield, based on ANTLR-Eclipse plugin
* by Torsten Juergeleit.
* 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
* Torsten Juergeleit - original ANTLR Eclipse plugin
* Scott Stanchfield - modifications for ANTXR
*******************************************************************************/
package com.javadude.antxr.eclipse.ui.editor.text;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextDoubleClickStrategy;
import org.eclipse.jface.text.ITextViewer;
import com.javadude.antxr.eclipse.ui.AntxrUIPlugin;
/**
* Double click strategy aware of ANTXR identifier syntax rules.
*/
public class DoubleClickStrategy implements ITextDoubleClickStrategy {
protected ITextViewer fText;
protected int fPos;
protected int fStartPos;
protected int fEndPos;
protected static final char[] BRACKETS = { '{', '}', '(', ')', '[', ']',
'"', '"' };
/** {@inheritDoc} */
public void doubleClicked(ITextViewer aText) {
fPos = aText.getSelectedRange().x;
if (fPos >= 0) {
fText = aText;
if (!selectBracketBlock()) {
selectWord();
}
}
}
/**
* Select the area between the selected bracket and the closing bracket.
* @return true if successful
*/
protected boolean selectBracketBlock() {
if (matchBracketsAt()) {
if (fStartPos == fEndPos) {
fText.setSelectedRange(fStartPos, 0);
} else {
fText.setSelectedRange(fStartPos + 1, fEndPos - fStartPos - 1);
}
return true;
}
return false;
}
/**
* Select the word at the current selection.
*/
protected void selectWord() {
if (matchWord()) {
if (fStartPos == fEndPos) {
fText.setSelectedRange(fStartPos, 0);
} else {
fText.setSelectedRange(fStartPos + 1, fEndPos - fStartPos - 1);
}
}
}
/**
* Match the brackets at the current selection.
* @return true if successful, false otherwise.
*/
protected boolean matchBracketsAt() {
char prevChar, nextChar;
int i;
int bracketIndex1 = DoubleClickStrategy.BRACKETS.length;
int bracketIndex2 = DoubleClickStrategy.BRACKETS.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 < DoubleClickStrategy.BRACKETS.length; i += 2) {
if (prevChar == DoubleClickStrategy.BRACKETS[i]) {
fStartPos = fPos - 1;
bracketIndex1 = i;
}
}
for (i = 1; i < DoubleClickStrategy.BRACKETS.length; i += + 2) {
if (nextChar == DoubleClickStrategy.BRACKETS[i]) {
fEndPos = fPos;
bracketIndex2 = i;
}
}
if (fStartPos > -1 && bracketIndex1 < bracketIndex2) {
fEndPos = searchForClosingBracket(fStartPos, prevChar,
DoubleClickStrategy.BRACKETS[bracketIndex1 + 1], doc);
if (fEndPos > -1) {
return true;
}
fStartPos= -1;
} else if (fEndPos > -1) {
fStartPos= searchForOpenBracket(fEndPos, DoubleClickStrategy.BRACKETS[bracketIndex2 - 1],
nextChar, doc);
if (fStartPos > -1) {
return true;
}
fEndPos= -1;
}
} catch (BadLocationException e) {
AntxrUIPlugin.log(e);
}
return false;
}
/**
* Select the word at the current selection.
* @return true if successful, false otherwise.
*/
protected boolean matchWord() {
IDocument doc= fText.getDocument();
try {
int pos = fPos;
char c;
while (pos >= 0) {
c = doc.getChar(pos);
if (!isWordPart(c)) {
break;
}
--pos;
}
fStartPos = pos;
pos = fPos;
int length = doc.getLength();
while (pos < length) {
c = doc.getChar(pos);
if (!isWordPart(c)) {
break;
}
++pos;
}
fEndPos= pos;
return true;
} catch (BadLocationException e) {
AntxrUIPlugin.log(e);
}
return false;
}
protected boolean isWordPart(char aChar) {
return Character.isLetterOrDigit(aChar) || aChar == '_';
}
/**
* Returns the position of the closing bracket after startPosition.
*
* @param aStartPosition the beginning position
* @param anOpenBracket the character that represents the open bracket
* @param aCloseBracket the character that represents the close bracket
* @param aDocument the document being searched
* @return the location of the closing bracket.
* @throws BadLocationException if the document position is invalid
*/
protected int searchForClosingBracket(int aStartPosition, char anOpenBracket,
char aCloseBracket, IDocument aDocument) throws BadLocationException {
int stack = 1;
int closePosition = aStartPosition + 1;
int length = aDocument.getLength();
char nextChar;
while (closePosition < length && stack > 0) {
nextChar= aDocument.getChar(closePosition);
if (nextChar == anOpenBracket && nextChar != aCloseBracket) {
stack++;
} else if (nextChar == aCloseBracket) {
stack--;
}
closePosition++;
}
if (stack == 0) {
return closePosition - 1;
}
return -1;
}
/**
* Returns the position of the open bracket before startPosition.
*
* @param startPosition - the beginning position
* @param openBracket - the character that represents the open bracket
* @param closeBracket - the character that represents the close bracket
* @param document - the document being searched
* @return the location of the starting bracket.
* @throws BadLocationException if the document position is invalid
*/
protected int searchForOpenBracket(int startPosition, char openBracket, char closeBracket, IDocument document) throws BadLocationException {
int stack= 1;
int openPos= startPosition - 1;
char nextChar;
while (openPos >= 0 && stack > 0) {
nextChar= document.getChar(openPos);
if (nextChar == closeBracket && nextChar != openBracket) {
stack++;
} else if (nextChar == openBracket) {
stack--;
}
openPos--;
}
if (stack == 0) {
return openPos + 1;
}
return -1;
}
}