| 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; |
| } |
| } |