/*
* Copyright (c) 2011, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.dart.tools.ui.internal.text.dart;
import com.google.dart.tools.ui.DartUiDebug;
import com.google.dart.tools.ui.DartX;
import com.google.dart.tools.ui.PreferenceConstants;
import com.google.dart.tools.ui.internal.text.editor.SemanticHighlightings;
import com.google.dart.tools.ui.internal.text.functions.AbstractDartScanner;
import com.google.dart.tools.ui.internal.text.functions.CombinedWordRule;
import com.google.dart.tools.ui.internal.text.functions.DartWhitespaceDetector;
import com.google.dart.tools.ui.internal.text.functions.DartWordDetector;
import com.google.dart.tools.ui.internal.text.functions.ISourceVersionDependent;
import com.google.dart.tools.ui.text.IColorManager;
import com.google.dart.tools.ui.text.IDartColorConstants;
import static com.google.dart.compiler.parser.Token.BREAK;
import static com.google.dart.compiler.parser.Token.IS;
import static com.google.dart.compiler.parser.Token.LIBRARY;
import static com.google.dart.compiler.parser.Token.NATIVE;
import static com.google.dart.compiler.parser.Token.WITH;
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.SingleLineRule;
import org.eclipse.jface.text.rules.Token;
import org.eclipse.jface.text.rules.WhitespaceRule;
import org.eclipse.jface.util.PropertyChangeEvent;
import java.util.ArrayList;
import java.util.List;
/**
* A Dart code scanner.
*/
public class DartCodeScanner extends AbstractDartScanner {
/**
* Rule to detect Dart brackets.
*/
private static class BracketRule implements IRule {
/** Dart brackets */
private static final char[] DART_BRACKETS = {'(', ')', '{', '}', '[', ']'};
/** Token to return for this rule */
private final IToken fToken;
/**
* Creates a new bracket rule.
*
* @param token Token to use for this rule
*/
public BracketRule(IToken token) {
fToken = token;
}
@Override
public IToken evaluate(ICharacterScanner scanner) {
int character = scanner.read();
if (isBracket((char) character)) {
do {
character = scanner.read();
} while (isBracket((char) character));
scanner.unread();
return fToken;
} else {
scanner.unread();
return Token.UNDEFINED;
}
}
/**
* Is this character a bracket character?
*
* @param character Character to determine whether it is a bracket character
* @return <code>true</code> iff the character is a bracket, <code>false</code> otherwise.
*/
public boolean isBracket(char character) {
for (int index = 0; index < DART_BRACKETS.length; index++) {
if (DART_BRACKETS[index] == character) {
return true;
}
}
return false;
}
}
/**
* Rule to detect Dart directives.
*/
private static class DirectiveRule implements IRule {
char[] sequence;
Token token;
DirectiveRule(String name, Token token) {
this.sequence = name.toCharArray();
this.token = token;
}
@Override
public IToken evaluate(ICharacterScanner scanner) {
int c = scanner.read();
if (c == '#') {
c = scanner.read();
int readCount = 2;
if (c == sequence[0]) {
for (int i = 1; i < sequence.length; i++) {
if (sequence[i] != scanner.read()) {
while (readCount-- > 0) {
scanner.unread();
}
return Token.UNDEFINED;
}
readCount += 1;
}
return token;
} else {
scanner.unread();
}
}
scanner.unread();
return Token.UNDEFINED;
}
}
/**
* Rule to detect Dart operators.
*/
private static class OperatorRule implements IRule {
/** Dart operators */
private static final char[] DART_OPERATORS = {
';', '.', '=', '/', '\\', '+', '-', '*', '<', '>', ':', '?', '!', ',', '|', '&', '^', '%',
'~'};
/** Token to return for this rule */
private final IToken fToken;
/**
* Creates a new operator rule.
*
* @param token Token to use for this rule
*/
public OperatorRule(IToken token) {
fToken = token;
}
@Override
public IToken evaluate(ICharacterScanner scanner) {
int character = scanner.read();
if (isOperator((char) character)) {
do {
character = scanner.read();
} while (isOperator((char) character));
scanner.unread();
return fToken;
} else {
scanner.unread();
return Token.UNDEFINED;
}
}
/**
* Is this character an operator character?
*
* @param character Character to determine whether it is an operator character
* @return <code>true</code> iff the character is an operator, <code>false</code> otherwise.
*/
public boolean isOperator(char character) {
for (int index = 0; index < DART_OPERATORS.length; index++) {
if (DART_OPERATORS[index] == character) {
return true;
}
}
return false;
}
}
@SuppressWarnings("unused")
private static class VersionedWordMatcher extends CombinedWordRule.WordMatcher implements
ISourceVersionDependent {
private final IToken fDefaultToken;
private final String fVersion;
private boolean fIsVersionMatch;
public VersionedWordMatcher(IToken defaultToken, String version, String currentVersion) {
fDefaultToken = defaultToken;
fVersion = version;
setSourceVersion(currentVersion);
}
@Override
public IToken evaluate(ICharacterScanner scanner, CombinedWordRule.CharacterBuffer word) {
IToken token = super.evaluate(scanner, word);
if (fIsVersionMatch || token.isUndefined()) {
return token;
}
return fDefaultToken;
}
@Override
public void setSourceVersion(String version) {
fIsVersionMatch = fVersion.compareTo(version) <= 0;
}
}
public static final String[] DIRECTIVES;
public static final String[] KEYWORDS;
public static final String[] PSEUDO_KEYWORDS;
public static final String[] OPERATORS;
static {
List<String> directives = new ArrayList<String>();
List<String> keywords = new ArrayList<String>();
List<String> pseudoKeywords = new ArrayList<String>();
List<String> operators = new ArrayList<String>();
com.google.dart.compiler.parser.Token[] tokens = com.google.dart.compiler.parser.Token.values();
for (int i = 0; i < tokens.length; i++) {
com.google.dart.compiler.parser.Token token = tokens[i];
if ((BREAK.ordinal() <= token.ordinal() && token.ordinal() <= WITH.ordinal())
|| token.ordinal() == IS.ordinal()) {
keywords.add(token.getSyntax());
}
if ((LIBRARY.ordinal() <= token.ordinal()) && (token.ordinal() <= NATIVE.ordinal())) {
String name = token.getSyntax();
directives.add(name.substring(1));
}
if (token.isBinaryOperator() || token.isUnaryOperator()) {
operators.add(token.getSyntax());
}
}
DIRECTIVES = directives.toArray(new String[directives.size()]);
KEYWORDS = keywords.toArray(new String[keywords.size()]);
PSEUDO_KEYWORDS = pseudoKeywords.toArray(new String[pseudoKeywords.size()]);
OPERATORS = operators.toArray(new String[operators.size()]);
}
private static final String RETURN = "return"; //$NON-NLS-1$
private static String[] fgConstants = {"false", "null", "true"}; //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$
private static final String ANNOTATION_BASE_KEY = PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_PREFIX
+ SemanticHighlightings.ANNOTATION;
private static final String ANNOTATION_COLOR_KEY = ANNOTATION_BASE_KEY
+ PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_COLOR_SUFFIX;
private static String[] fgTokenProperties = {
IDartColorConstants.JAVA_KEYWORD, IDartColorConstants.JAVA_STRING,
IDartColorConstants.JAVA_DEFAULT, IDartColorConstants.JAVA_KEYWORD_RETURN,
IDartColorConstants.JAVA_OPERATOR, IDartColorConstants.JAVA_BRACKET, ANNOTATION_COLOR_KEY,};
private boolean useSyntaxticHighlighter;
/**
* Creates a Dart code scanner
*
* @param manager the color manager
* @param store the preference store
*/
public DartCodeScanner(IColorManager manager, IPreferenceStore store) {
this(manager, store, !DartUiDebug.USE_ONLY_SEMANTIC_HIGHLIGHTER);
}
public DartCodeScanner(IColorManager manager, IPreferenceStore store,
boolean useSyntaxticHighlighter) {
super(manager, store);
this.useSyntaxticHighlighter = useSyntaxticHighlighter;
initialize();
}
@Override
public void adaptToPreferenceChange(PropertyChangeEvent event) {
DartX.todo();
super.adaptToPreferenceChange(event);
}
@Override
public boolean affectsBehavior(PropertyChangeEvent event) {
DartX.todo();
return super.affectsBehavior(event);
}
@Override
protected List<IRule> createRules() {
List<IRule> rules = new ArrayList<IRule>();
Token token;
// Add rule for character constants.
if (useSyntaxticHighlighter) {
token = getToken(IDartColorConstants.JAVA_STRING);
rules.add(new SingleLineRule("'", "'", token, '\\')); //$NON-NLS-2$ //$NON-NLS-1$
}
// Add generic whitespace rule.
rules.add(new WhitespaceRule(new DartWhitespaceDetector()));
// Add word rule for new keywords, 4077
DartWordDetector wordDetector = new DartWordDetector();
token = getToken(IDartColorConstants.JAVA_DEFAULT);
CombinedWordRule combinedWordRule = new CombinedWordRule(wordDetector, token);
// Add rule for operators
token = getToken(IDartColorConstants.JAVA_OPERATOR);
rules.add(new OperatorRule(token));
// Add rule for brackets
token = getToken(IDartColorConstants.JAVA_BRACKET);
rules.add(new BracketRule(token));
// Add word rule for keyword 'return'.
if (useSyntaxticHighlighter) {
CombinedWordRule.WordMatcher returnWordRule = new CombinedWordRule.WordMatcher();
token = getToken(IDartColorConstants.JAVA_KEYWORD_RETURN);
returnWordRule.addWord(RETURN, token);
combinedWordRule.addWordMatcher(returnWordRule);
}
// Add word rule for keywords and constants.
CombinedWordRule.WordMatcher wordRule = new CombinedWordRule.WordMatcher();
token = getToken(IDartColorConstants.JAVA_KEYWORD);
for (int i = 0; i < KEYWORDS.length; i++) {
wordRule.addWord(KEYWORDS[i], token);
}
if (useSyntaxticHighlighter) {
for (int i = 0; i < PSEUDO_KEYWORDS.length; i++) {
wordRule.addWord(PSEUDO_KEYWORDS[i], token);
}
}
for (int i = 0; i < fgConstants.length; i++) {
wordRule.addWord(fgConstants[i], token);
}
if (useSyntaxticHighlighter) {
for (int i = 0; i < DIRECTIVES.length; i++) {
rules.add(new DirectiveRule(DIRECTIVES[i], token));
}
rules.add(new DirectiveRule("!", token));
}
combinedWordRule.addWordMatcher(wordRule);
rules.add(combinedWordRule);
setDefaultReturnToken(getToken(IDartColorConstants.JAVA_DEFAULT));
return rules;
}
@Override
protected String getBoldKey(String colorKey) {
if ((ANNOTATION_COLOR_KEY).equals(colorKey)) {
return ANNOTATION_BASE_KEY + PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_BOLD_SUFFIX;
}
return super.getBoldKey(colorKey);
}
@Override
protected String getItalicKey(String colorKey) {
if ((ANNOTATION_COLOR_KEY).equals(colorKey)) {
return ANNOTATION_BASE_KEY + PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_ITALIC_SUFFIX;
}
return super.getItalicKey(colorKey);
}
@Override
protected String getStrikethroughKey(String colorKey) {
if ((ANNOTATION_COLOR_KEY).equals(colorKey)) {
return ANNOTATION_BASE_KEY
+ PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_STRIKETHROUGH_SUFFIX;
}
return super.getStrikethroughKey(colorKey);
}
@Override
protected String[] getTokenProperties() {
return fgTokenProperties;
}
@Override
protected String getUnderlineKey(String colorKey) {
if ((ANNOTATION_COLOR_KEY).equals(colorKey)) {
return ANNOTATION_BASE_KEY
+ PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_UNDERLINE_SUFFIX;
}
return super.getUnderlineKey(colorKey);
}
}