blob: 9ad24ce007e39cb1d4d0fad280b9368bcdff0545 [file] [log] [blame]
package org.eclipse.dltk.ruby.internal.ui.text;
import java.util.Arrays;
import org.eclipse.core.runtime.Assert;
import org.eclipse.dltk.ui.DLTKUIPlugin;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.Region;
public class RubyHeuristicScanner extends ScriptHeuristicScanner implements
IRubySymbols {
private static final int[] BLOCK_BEGINNING_KEYWORDS = { TokenIF, TokenFOR,
TokenDEF, TokenCASE, TokenCATCH, TokenCLASS, TokenWHILE,
TokenBEGIN, TokenUNTIL, TokenUNLESS, TokenMODULE, TokenDO };
private static final int[] BLOCK_BEGINNING_SYMBOLS = { TokenLBRACE,
TokenLBRACKET };
private static final int[] BLOCK_MIDDLES = { TokenELSE, TokenELSIF,
TokenENSURE, TokenRESCUE, TokenWHEN };
private static final int[] BLOCK_ENDINGS = { TokenEND, TokenRBRACE,
TokenRBRACKET };
static {
Arrays.sort(BLOCK_BEGINNING_KEYWORDS);
Arrays.sort(BLOCK_BEGINNING_SYMBOLS);
Arrays.sort(BLOCK_MIDDLES);
Arrays.sort(BLOCK_ENDINGS);
}
/**
* Calls
* <code>super(document, IRubyPartitions.RUBY_PARTITIONING, IDocument.DEFAULT_CONTENT_TYPE)</code>
* .
*
* @param document
* the document to scan.
*/
public RubyHeuristicScanner(IDocument document) {
super(document, IRubyPartitions.RUBY_PARTITIONING,
IDocument.DEFAULT_CONTENT_TYPE);
}
@Override
public int getToken(int pos, String s) {
Assert.isNotNull(s);
switch (s.length()) {
case 2:
if ("if".equals(s)) //$NON-NLS-1$
return TokenIF;
if ("do".equals(s)) //$NON-NLS-1$
return TokenDO;
if ("in".equals(s)) //$NON-NLS-1$
return TokenIN;
if ("or".equals(s)) //$NON-NLS-1$
return TokenOR;
break;
case 3:
if ("for".equals(s)) //$NON-NLS-1$
return TokenFOR;
if ("and".equals(s)) //$NON-NLS-1$
return TokenAND;
if ("def".equals(s)) //$NON-NLS-1$
return TokenDEF;
if ("end".equals(s)) { //$NON-NLS-1$
if (pos > 0 && getChar(pos - 1) == '=') {
return TokenRDOCEND;
}
return TokenEND;
}
if ("END".equals(s)) //$NON-NLS-1$
return TokenEND;
if ("nil".equals(s)) //$NON-NLS-1$
return TokenNIL;
if ("not".equals(s)) //$NON-NLS-1$
return TokenNOT;
break;
case 4:
if ("case".equals(s)) //$NON-NLS-1$
return TokenCASE;
if ("else".equals(s)) //$NON-NLS-1$
return TokenELSE;
if ("when".equals(s)) //$NON-NLS-1$
return TokenWHEN;
if ("then".equals(s)) //$NON-NLS-1$
return TokenTHEN;
if ("next".equals(s)) //$NON-NLS-1$
return TokenNEXT;
if ("redo".equals(s)) //$NON-NLS-1$
return TokenREDO;
break;
case 5:
if ("break".equals(s)) //$NON-NLS-1$
return TokenBREAK;
if ("catch".equals(s)) //$NON-NLS-1$
return TokenCATCH;
if ("class".equals(s)) //$NON-NLS-1$
return TokenCLASS;
if ("while".equals(s)) //$NON-NLS-1$
return TokenWHILE;
if ("alias".equals(s)) //$NON-NLS-1$
return TokenALIAS;
if ("BEGIN".equals(s)) //$NON-NLS-1$
return TokenBEGIN;
if ("begin".equals(s)) { //$NON-NLS-1$
if (pos > 0 && getChar(pos - 1) == '=') {
return TokenRDOCBEGIN;
}
return TokenBEGIN;
}
if ("elsif".equals(s)) //$NON-NLS-1$
return TokenELSIF;
if ("retry".equals(s)) //$NON-NLS-1$
return TokenRETRY;
if ("undef".equals(s)) //$NON-NLS-1$
return TokenUNDEF;
if ("until".equals(s)) //$NON-NLS-1$
return TokenUNTIL;
if ("yield".equals(s)) //$NON-NLS-1$
return TokenYIELD;
break;
case 6:
if ("return".equals(s)) //$NON-NLS-1$
return TokenRETURN;
if ("ensure".equals(s)) //$NON-NLS-1$
return TokenENSURE;
if ("rescue".equals(s)) //$NON-NLS-1$
return TokenRESCUE;
if ("unless".equals(s)) //$NON-NLS-1$
return TokenUNLESS;
if ("module".equals(s)) //$NON-NLS-1$
return TokenMODULE;
break;
case 7:
if ("defined".equals(s)) //$NON-NLS-1$
return TokenDEFINED;
break;
}
return TokenIDENTIFIER;
}
private char getChar(int pos) {
try {
return getDocument().getChar(pos);
} catch (BadLocationException e) {
DLTKUIPlugin.log(e);
return 0;
}
}
public IRegion findSurroundingBlock(int offset) {
int start = findBlockBeginningOffset(offset);
if (start == NOT_FOUND)
start = 0;
int end = findBlockEndingOffset(offset);
if (end == NOT_FOUND)
end = getDocument().getLength();
return new Region(start, end - start);
}
public boolean isBlockBeginning(int offset, int bound) {
int token = previousToken(bound, offset);
while (token != NOT_FOUND) {
if (Arrays.binarySearch(BLOCK_BEGINNING_SYMBOLS, token) >= 0)
return true;
if (Arrays.binarySearch(BLOCK_BEGINNING_KEYWORDS, token) >= 0) {
int pos = getPosition();
int prevToken = token;
token = previousToken(getPosition(), offset);
if (token == NOT_FOUND || token == TokenEQUAL
|| prevToken == TokenDO) {
setPosition(pos + 1);
return true;
}
}
token = previousToken(getPosition(), offset);
}
return false;
}
public boolean isBlockMiddle(int offset, int bound) {
if (Arrays.binarySearch(BLOCK_MIDDLES, nextToken(offset, bound)) >= 0) {
// setting the position to start of the block keyword
findNonIdentifierBackward(offset, UNBOUND);
setPosition(getPosition() + 1);
return true;
} else {
return false;
}
}
public boolean isBlockEnding(int offset, int bound) {
int token = nextToken(offset, bound);
while (token != NOT_FOUND) {
if (Arrays.binarySearch(BLOCK_ENDINGS, token) >= 0)
return true;
token = nextToken(getPosition(), bound);
}
return false;
}
public int findBlockBeginningOffset(int offset) {
try {
IDocument d = getDocument();
int line = d.getLineOfOffset(offset);
int endingCount = 0;
while (line >= 0) {
IRegion info = d.getLineInformation(line);
int start = info.getOffset();
int end = Math.min(info.getOffset() + info.getLength(), offset);
setPosition(start);
while (getPosition() < end) {
if (isBlockEnding(getPosition(), end)) {
endingCount++;
}
}
start = info.getOffset();
end = Math.min(info.getOffset() + info.getLength(), offset);
setPosition(end);
while (getPosition() > start) {
if (isBlockBeginning(start, getPosition())) {
if (endingCount > 0) {
endingCount--;
} else {
return getPosition();
}
}
}
line--;
}
} catch (BadLocationException e) {
DLTKUIPlugin.log(e);
}
return NOT_FOUND;
}
public int findBlockEndingOffset(int offset) {
try {
IDocument d = getDocument();
int line = d.getLineOfOffset(offset);
int lineNum = d.getNumberOfLines();
int beginningCount = 0;
while (line < lineNum) {
IRegion info = d.getLineInformation(line);
int start = Math.max(info.getOffset(), offset);
int end = info.getOffset() + info.getLength();
setPosition(end);
while (getPosition() > start) {
if (isBlockBeginning(start, getPosition())) {
beginningCount++;
}
}
start = Math.max(info.getOffset(), offset);
end = info.getOffset() + info.getLength();
setPosition(start);
while (getPosition() < end) {
if (isBlockEnding(getPosition(), end)) {
if (beginningCount > 0) {
beginningCount--;
} else {
return getPosition();
}
}
}
line++;
}
} catch (BadLocationException e) {
DLTKUIPlugin.log(e);
}
return NOT_FOUND;
}
public int previousTokenAfterInput(int offset, String appended) {
try {
if (getPartition(offset).getType() != IDocument.DEFAULT_CONTENT_TYPE)
return NOT_FOUND;
if (appended.length() == 1) {
int token = getGenericToken(appended.charAt(0));
if (token != TokenOTHER)
return token;
}
IRegion line = getDocument().getLineInformationOfOffset(offset);
String content = getDocument().get(line.getOffset(),
offset - line.getOffset())
+ appended;
IDocument newDoc = new Document(content);
RubyHeuristicScanner scanner = new RubyHeuristicScanner(newDoc);
return scanner.previousToken(content.length(), UNBOUND);
} catch (BadLocationException e) {
DLTKUIPlugin.log(e);
}
return NOT_FOUND;
}
}