/******************************************************************************* * Copyright (c) 2009, 2012 Sierra Wireless 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: * Sierra Wireless - initial API and implementation *******************************************************************************/ package org.eclipse.koneki.ldt.ui.internal.editor.text; import java.util.ArrayList; import java.util.List; import org.eclipse.dltk.ui.text.AbstractScriptScanner; import org.eclipse.dltk.ui.text.IColorManager; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.rules.ICharacterScanner; import org.eclipse.jface.text.rules.IRule; import org.eclipse.jface.text.rules.IToken; import org.eclipse.jface.text.rules.IWhitespaceDetector; import org.eclipse.jface.text.rules.IWordDetector; import org.eclipse.jface.text.rules.NumberRule; import org.eclipse.jface.text.rules.Token; import org.eclipse.jface.text.rules.WhitespaceRule; import org.eclipse.jface.text.rules.WordRule; public class LuaCodeScanner extends AbstractScriptScanner { @SuppressWarnings("nls") private static String[] fgKeywords = { "and", "break", "do", "else", "elseif", "end", "false", "for", "function", "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while" }; private static String[] fgTokenProperties = new String[] { ILuaColorConstants.LUA_NUMBER, ILuaColorConstants.LUA_DEFAULT, ILuaColorConstants.LUA_KEYWORD }; public LuaCodeScanner(IColorManager manager, IPreferenceStore store) { super(manager, store); this.initialize(); } protected String[] getTokenProperties() { return fgTokenProperties; } protected List<IRule> createRules() { final List<IRule> rules = new ArrayList<IRule>(); final IToken keyword = this.getToken(ILuaColorConstants.LUA_KEYWORD); final IToken numbers = this.getToken(ILuaColorConstants.LUA_NUMBER); final IToken other = this.getToken(ILuaColorConstants.LUA_DEFAULT); // Add generic whitespace rule. rules.add(new WhitespaceRule(new LuaWhitespaceDetector())); // Add word rule for keywords. final WordRule wordRule = new WordRule(new LuaWordDetector(), other); for (int i = 0; i < fgKeywords.length; i++) { wordRule.addWord(fgKeywords[i], keyword); } rules.add(wordRule); // Add number recognition final NumberRule numberRule = new LuaNumberRule(numbers); rules.add(numberRule); // Default case this.setDefaultReturnToken(other); return rules; } /** * Indicates if argument is a white space * * @param char Tested character */ public static class LuaWhitespaceDetector implements IWhitespaceDetector { public boolean isWhitespace(char character) { return Character.isWhitespace(character); } } public static class LuaWordDetector implements IWordDetector { /** * Indicates if argument is part of a word * * @param char Tested character */ public boolean isWordPart(char character) { return Character.isJavaIdentifierPart(character); } /** * Indicates if argument starts of a word * * @param char Tested character */ public boolean isWordStart(char character) { return Character.isJavaIdentifierStart(character); } } public static class LuaNumberRule extends NumberRule { public LuaNumberRule(final IToken token) { super(token); } @Override public IToken evaluate(final ICharacterScanner scanner) { if (eatNumber(scanner) > 0) { return fToken; } return Token.UNDEFINED; } private static int eatExponential(final ICharacterScanner scanner) { // Find 'e' or 'E' char current = (char) scanner.read(); if (current == 'e' || current == 'E') { // 'e' is followed by digits it is an exponential notation if (followedByDigit(scanner)) { return eatDecimalDigits(scanner) + 1; } else { // Check for optional sign current = (char) scanner.read(); if ((current == '-' || current == '+') && followedByDigit(scanner)) { // There is a digit after 'e' and sign return eatDecimalDigits(scanner) + 2; } else { // Last characters red are not an exponential notation scanner.unread(); } } } scanner.unread(); return 0; } private static int eatDecimalDigitsFromDot(final ICharacterScanner scanner) { // Handle '.' if (scanner.read() == '.') { return eatDecimalDigits(scanner) + 1; } scanner.unread(); return 0; } private static int eatDecimalDigits(final ICharacterScanner scanner) { int digits = 0; while (Character.isDigit((char) scanner.read())) { digits++; } scanner.unread(); return digits; } private static int eatNumber(final ICharacterScanner scanner) { final char current = (char) scanner.read(); final int result; switch (current) { case '.': result = eatDecimalDigits(scanner); if (result > 0) { return result + eatExponential(scanner) + 1; } break; case '0': // Check hexadecimal if (followedByChar(scanner, 'x') || followedByChar(scanner, 'X')) { result = eatHexaecimalDigits(scanner); if (result > 0) { return result + eatExponential(scanner) + 1; } } else { // Regular numbers return eatDecimalDigits(scanner) + eatDecimalDigitsFromDot(scanner) + eatExponential(scanner) + 1; } break; default: if (Character.isDigit(current)) { return eatDecimalDigits(scanner) + eatDecimalDigitsFromDot(scanner) + eatExponential(scanner) + 1; } } scanner.unread(); return 0; } private static int eatHexaecimalDigits(final ICharacterScanner scanner) { // Find 'x' int digits = 0; final char current = (char) scanner.read(); if ((current == 'x' || current == 'X') && followedByHexadecimal(scanner)) { digits++; } else { scanner.unread(); return digits; } // Loop over hexadecimal digits while (Character.digit((char) scanner.read(), 16) != -1) { digits++; } scanner.unread(); return digits; } private static boolean followedByChar(final ICharacterScanner scanner, final char character) { final boolean result = character == (char) scanner.read(); scanner.unread(); return result; } private static boolean followedByDigit(final ICharacterScanner scanner) { final boolean result = Character.isDigit((char) scanner.read()); scanner.unread(); return result; } private static boolean followedByHexadecimal(final ICharacterScanner scanner) { final boolean result = Character.digit((char) scanner.read(), 16) != -1; scanner.unread(); return result; } } }