| /******************************************************************************* |
| * Copyright (c) 2000, 2008 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Philippe Ombredanne <pombredanne@nexb.com> - https://bugs.eclipse.org/bugs/show_bug.cgi?id=150989 |
| * Anton Leherbauer (Wind River Systems) - [misc] Allow custom token for WhitespaceRule - https://bugs.eclipse.org/bugs/show_bug.cgi?id=251224 |
| *******************************************************************************/ |
| package org.eclipse.e4.tools.emf.ui.script.js.text.scanners; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.e4.tools.emf.ui.script.js.ResourceProvider; |
| import org.eclipse.e4.tools.services.IResourcePool; |
| 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; |
| |
| |
| /** |
| * A Java code scanner. |
| */ |
| public final class JavaScriptCodeScanner extends AbstractJavaScanner { |
| |
| /** |
| * Rule to detect java operators. |
| * |
| * @since 3.0 |
| */ |
| private static final class OperatorRule implements IRule { |
| |
| /** Java operators */ |
| private final char[] JAVA_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; |
| } |
| |
| /** |
| * 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 < JAVA_OPERATORS.length; index++) { |
| if (JAVA_OPERATORS[index] == character) |
| return true; |
| } |
| return false; |
| } |
| |
| /* |
| * @see org.eclipse.jface.text.rules.IRule#evaluate(org.eclipse.jface.text.rules.ICharacterScanner) |
| */ |
| @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; |
| } |
| } |
| } |
| |
| /** |
| * Rule to detect java brackets. |
| * |
| * @since 3.3 |
| */ |
| private static final class BracketRule implements IRule { |
| |
| /** Java brackets */ |
| private final char[] JAVA_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; |
| } |
| |
| /** |
| * 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 < JAVA_BRACKETS.length; index++) { |
| if (JAVA_BRACKETS[index] == character) |
| return true; |
| } |
| return false; |
| } |
| |
| /* |
| * @see org.eclipse.jface.text.rules.IRule#evaluate(org.eclipse.jface.text.rules.ICharacterScanner) |
| */ |
| @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; |
| } |
| } |
| } |
| |
| |
| // 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); |
| // } |
| // |
| // /* |
| // * @see org.eclipse.jdt.internal.ui.text.ISourceVersionDependent#setSourceVersion(java.lang.String) |
| // */ |
| // public void setSourceVersion(String version) { |
| // fIsVersionMatch= fVersion.compareTo(version) <= 0; |
| // } |
| // |
| // /* |
| // * @see org.eclipse.jdt.internal.ui.text.CombinedWordRule.WordMatcher#evaluate(org.eclipse.jface.text.rules.ICharacterScanner, org.eclipse.jdt.internal.ui.text.CombinedWordRule.CharacterBuffer) |
| // */ |
| // public IToken evaluate(ICharacterScanner scanner, CombinedWordRule.CharacterBuffer word) { |
| // IToken token= super.evaluate(scanner, word); |
| // |
| // if (fIsVersionMatch || token.isUndefined()) |
| // return token; |
| // |
| // return fDefaultToken; |
| // } |
| // } |
| |
| // /** |
| // * An annotation rule matches the '@' symbol, any following whitespace and |
| // * optionally a following <code>interface</code> keyword. |
| // * |
| // * It does not match if there is a comment between the '@' symbol and |
| // * the identifier. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=82452 |
| // * |
| // * @since 3.1 |
| // */ |
| // private static class AnnotationRule implements IRule, ISourceVersionDependent { |
| // /** |
| // * A resettable scanner supports marking a position in a scanner and |
| // * unreading back to the marked position. |
| // */ |
| // private static final class ResettableScanner implements ICharacterScanner { |
| // private final ICharacterScanner fDelegate; |
| // private int fReadCount; |
| // |
| // /** |
| // * Creates a new resettable scanner that will forward calls |
| // * to <code>scanner</code>, but store a marked position. |
| // * |
| // * @param scanner the delegate scanner |
| // */ |
| // public ResettableScanner(final ICharacterScanner scanner) { |
| // fDelegate= scanner; |
| // mark(); |
| // } |
| // |
| // /* |
| // * @see org.eclipse.jface.text.rules.ICharacterScanner#getColumn() |
| // */ |
| // public int getColumn() { |
| // return fDelegate.getColumn(); |
| // } |
| // |
| // /* |
| // * @see org.eclipse.jface.text.rules.ICharacterScanner#getLegalLineDelimiters() |
| // */ |
| // public char[][] getLegalLineDelimiters() { |
| // return fDelegate.getLegalLineDelimiters(); |
| // } |
| // |
| // /* |
| // * @see org.eclipse.jface.text.rules.ICharacterScanner#read() |
| // */ |
| // public int read() { |
| // int ch= fDelegate.read(); |
| // if (ch != ICharacterScanner.EOF) |
| // fReadCount++; |
| // return ch; |
| // } |
| // |
| // /* |
| // * @see org.eclipse.jface.text.rules.ICharacterScanner#unread() |
| // */ |
| // public void unread() { |
| // if (fReadCount > 0) |
| // fReadCount--; |
| // fDelegate.unread(); |
| // } |
| // |
| // /** |
| // * Marks an offset in the scanned content. |
| // */ |
| // public void mark() { |
| // fReadCount= 0; |
| // } |
| // |
| // /** |
| // * Resets the scanner to the marked position. |
| // */ |
| // public void reset() { |
| // while (fReadCount > 0) |
| // unread(); |
| // |
| // while (fReadCount < 0) |
| // read(); |
| // } |
| // } |
| // |
| // private final IWhitespaceDetector fWhitespaceDetector= new JavaScriptWhitespaceDetector(); |
| // private final IWordDetector fWordDetector= new JavaScriptWordDetector(); |
| // private final IToken fInterfaceToken; |
| // private final IToken fAtToken; |
| // private final String fVersion; |
| // private boolean fIsVersionMatch; |
| // |
| // /** |
| // * Creates a new rule. |
| // * |
| // * @param interfaceToken the token to return if |
| // * <code>'@\s*interface'</code> is matched |
| // * @param atToken the token to return if <code>'@'</code> |
| // * is matched, but not <code>'@\s*interface'</code> |
| // * @param version the lowest <code>JavaCore.COMPILER_SOURCE</code> |
| // * version that this rule is enabled |
| // * @param currentVersion the current |
| // * <code>JavaCore.COMPILER_SOURCE</code> version |
| // */ |
| // public AnnotationRule(IToken interfaceToken, Token atToken, String version, String currentVersion) { |
| // fInterfaceToken= interfaceToken; |
| // fAtToken= atToken; |
| // fVersion= version; |
| // setSourceVersion(currentVersion); |
| // } |
| // |
| // /* |
| // * @see org.eclipse.jface.text.rules.IRule#evaluate(org.eclipse.jface.text.rules.ICharacterScanner) |
| // */ |
| // public IToken evaluate(ICharacterScanner scanner) { |
| // if (!fIsVersionMatch) |
| // return Token.UNDEFINED; |
| // |
| // ResettableScanner resettable= new ResettableScanner(scanner); |
| // if (resettable.read() == '@') |
| // return readAnnotation(resettable); |
| // |
| // resettable.reset(); |
| // return Token.UNDEFINED; |
| // } |
| // |
| // private IToken readAnnotation(ResettableScanner scanner) { |
| // scanner.mark(); |
| // skipWhitespace(scanner); |
| // if (readInterface(scanner)) { |
| // return fInterfaceToken; |
| // } else { |
| // scanner.reset(); |
| // return fAtToken; |
| // } |
| // } |
| // |
| // private boolean readInterface(ICharacterScanner scanner) { |
| // int ch= scanner.read(); |
| // int i= 0; |
| // while (i < INTERFACE.length() && INTERFACE.charAt(i) == ch) { |
| // i++; |
| // ch= scanner.read(); |
| // } |
| // if (i < INTERFACE.length()) |
| // return false; |
| // |
| // if (fWordDetector.isWordPart((char) ch)) |
| // return false; |
| // |
| // if (ch != ICharacterScanner.EOF) |
| // scanner.unread(); |
| // |
| // return true; |
| // } |
| // |
| // private boolean skipWhitespace(ICharacterScanner scanner) { |
| // while (fWhitespaceDetector.isWhitespace((char) scanner.read())) { |
| // // do nothing |
| // } |
| // |
| // scanner.unread(); |
| // return true; |
| // } |
| // |
| // /* |
| // * @see org.eclipse.jdt.internal.ui.text.ISourceVersionDependent#setSourceVersion(java.lang.String) |
| // */ |
| // public void setSourceVersion(String version) { |
| // fIsVersionMatch= fVersion.compareTo(version) <= 0; |
| // } |
| // |
| // } |
| |
| // private static final String SOURCE_VERSION= JavaCore.COMPILER_SOURCE; |
| |
| /*static String[] fgKeywords= { |
| "abstract", //$NON-NLS-1$ |
| "break", //$NON-NLS-1$ |
| "case", "catch", "class", "const", "continue", //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ |
| "default", "do", //$NON-NLS-2$ //$NON-NLS-1$ |
| "else", "extends", //$NON-NLS-2$ //$NON-NLS-1$ |
| "final", "finally", "for", //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ |
| "goto", //$NON-NLS-1$ |
| "if", "implements", "import", "instanceof", "interface", //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ |
| "native", "new", //$NON-NLS-2$ //$NON-NLS-1$ |
| "package", "private", "protected", "public", //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ |
| "static", "super", "switch", "synchronized", //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ |
| "this", "throw", "throws", "transient", "try", //$NON-NLS-5$ //$NON-NLS-4$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$ |
| "volatile", //$NON-NLS-1$ |
| "while" //$NON-NLS-1$ |
| };*/ |
| |
| static String[] fgKeywords= { |
| "break", |
| "case", |
| "catch", |
| "continue", |
| "debugger", |
| "default", |
| "delete", |
| "do", |
| "else", |
| "finally", |
| "for", |
| "function", |
| "if", |
| "in", |
| "instanceof", |
| "new", |
| // "return", |
| "switch", |
| "this", |
| "throw", |
| "try", |
| "typeof", |
| "var", |
| "void", |
| "while", |
| "with", |
| "class", |
| "enum", |
| "export", |
| "extends", |
| "import", |
| "super" |
| |
| }; |
| |
| |
| // private static final String INTERFACE= "interface"; //$NON-NLS-1$ |
| private static final String RETURN= "return"; //$NON-NLS-1$ |
| // private static String[] fgJava14Keywords= { "assert" }; //$NON-NLS-1$ |
| // private static String[] fgJava15Keywords= { "enum" }; //$NON-NLS-1$ |
| |
| private static String[] fgTypes= { "void", "boolean", "char", "byte", "short", "strictfp", "int", "long", "float", "double" }; //$NON-NLS-1$ //$NON-NLS-5$ //$NON-NLS-7$ //$NON-NLS-6$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-2$ |
| |
| 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= "COLOR_org.eclipse.e4.tools.emf.ui.script.js." + ANNOTATION_BASE_KEY + PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_COLOR_SUFFIX; |
| |
| private static String[] fgTokenProperties= { |
| ResourceProvider.JAVA_KEYWORD, |
| ResourceProvider.JAVA_STRING, |
| ResourceProvider.JAVA_DEFAULT, |
| ResourceProvider.JAVA_KEYWORD_RETURN, |
| ResourceProvider.JAVA_OPERATOR, |
| ResourceProvider.JAVA_BRACKET/*, |
| ANNOTATION_COLOR_KEY,*/ |
| }; |
| |
| // private List<ISourceVersionDependent> fVersionDependentRules= new ArrayList<ISourceVersionDependent>(3); |
| |
| /** |
| * Creates a Java code scanner |
| * |
| * @param manager the color manager |
| * @param store the preference store |
| */ |
| public JavaScriptCodeScanner(IResourcePool manager, IPreferenceStore store) { |
| super(manager, store); |
| initialize(); |
| } |
| |
| /* |
| * @see AbstractJavaScanner#getTokenProperties() |
| */ |
| @Override |
| protected String[] getTokenProperties() { |
| return fgTokenProperties; |
| } |
| |
| /* |
| * @see AbstractJavaScanner#createRules() |
| */ |
| @Override |
| protected List<IRule> createRules() { |
| |
| List<IRule> rules= new ArrayList<IRule>(); |
| |
| // Add rule for character constants. |
| Token token= getToken(ResourceProvider.JAVA_STRING); |
| rules.add(new SingleLineRule("'", "'", token, '\\')); //$NON-NLS-2$ //$NON-NLS-1$ |
| |
| |
| Token defaultToken= getToken(ResourceProvider.JAVA_DEFAULT); |
| |
| // Add generic whitespace rule. |
| rules.add(new WhitespaceRule(new JavaScriptWhitespaceDetector(), defaultToken)); |
| |
| // String version= getPreferenceStore().getString(SOURCE_VERSION); |
| |
| // // Add JLS3 rule for /@\s*interface/ and /@\s*\w+/ |
| // token= getToken(ANNOTATION_COLOR_KEY); |
| // AnnotationRule atInterfaceRule= new AnnotationRule(getToken(IJavaScriptColorConstants.JAVA_KEYWORD), token, JavaCore.VERSION_1_5, version); |
| // rules.add(atInterfaceRule); |
| // fVersionDependentRules.add(atInterfaceRule); |
| // |
| // // Add word rule for new keywords, see bug 4077 |
| JavaScriptWordDetector wordDetector= new JavaScriptWordDetector(); |
| CombinedWordRule combinedWordRule= new CombinedWordRule(wordDetector, defaultToken); |
| // |
| // VersionedWordMatcher j14Matcher= new VersionedWordMatcher(defaultToken, JavaCore.VERSION_1_4, version); |
| // |
| // token= getToken(IJavaScriptColorConstants.JAVA_KEYWORD); |
| // for (int i=0; i<fgJava14Keywords.length; i++) |
| // j14Matcher.addWord(fgJava14Keywords[i], token); |
| // |
| // combinedWordRule.addWordMatcher(j14Matcher); |
| // fVersionDependentRules.add(j14Matcher); |
| // |
| // VersionedWordMatcher j15Matcher= new VersionedWordMatcher(defaultToken, JavaCore.VERSION_1_5, version); |
| // |
| // token= getToken(IJavaScriptColorConstants.JAVA_KEYWORD); |
| // for (int i=0; i<fgJava15Keywords.length; i++) |
| // j15Matcher.addWord(fgJava15Keywords[i], token); |
| // |
| // combinedWordRule.addWordMatcher(j15Matcher); |
| // fVersionDependentRules.add(j15Matcher); |
| |
| // Add rule for operators |
| token= getToken(ResourceProvider.JAVA_OPERATOR); |
| rules.add(new OperatorRule(token)); |
| |
| // Add rule for brackets |
| token= getToken(ResourceProvider.JAVA_BRACKET); |
| rules.add(new BracketRule(token)); |
| |
| // Add word rule for keyword 'return'. |
| CombinedWordRule.WordMatcher returnWordRule= new CombinedWordRule.WordMatcher(); |
| token= getToken(ResourceProvider.JAVA_KEYWORD_RETURN); |
| returnWordRule.addWord(RETURN, token); |
| combinedWordRule.addWordMatcher(returnWordRule); |
| |
| // Add word rule for keywords, types, and constants. |
| CombinedWordRule.WordMatcher wordRule= new CombinedWordRule.WordMatcher(); |
| token= getToken(ResourceProvider.JAVA_KEYWORD); |
| for (int i=0; i<fgKeywords.length; i++) |
| wordRule.addWord(fgKeywords[i], token); |
| for (int i=0; i<fgTypes.length; i++) |
| wordRule.addWord(fgTypes[i], token); |
| for (int i=0; i<fgConstants.length; i++) |
| wordRule.addWord(fgConstants[i], token); |
| |
| combinedWordRule.addWordMatcher(wordRule); |
| |
| rules.add(combinedWordRule); |
| |
| setDefaultReturnToken(defaultToken); |
| return rules; |
| } |
| |
| // /* |
| // * @see org.eclipse.jdt.internal.ui.text.AbstractJavaScanner#getBoldKey(java.lang.String) |
| // */ |
| // 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); |
| // } |
| // |
| // /* |
| // * @see org.eclipse.jdt.internal.ui.text.AbstractJavaScanner#getItalicKey(java.lang.String) |
| // */ |
| // 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); |
| // } |
| // |
| // /* |
| // * @see org.eclipse.jdt.internal.ui.text.AbstractJavaScanner#getStrikethroughKey(java.lang.String) |
| // */ |
| // 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); |
| // } |
| // |
| // /* |
| // * @see org.eclipse.jdt.internal.ui.text.AbstractJavaScanner#getUnderlineKey(java.lang.String) |
| // */ |
| // 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); |
| // } |
| // |
| // /* |
| // * @see AbstractJavaScanner#affectsBehavior(PropertyChangeEvent) |
| // */ |
| // public boolean affectsBehavior(PropertyChangeEvent event) { |
| // return event.getProperty().equals(SOURCE_VERSION) || super.affectsBehavior(event); |
| // } |
| // |
| // /* |
| // * @see AbstractJavaScanner#adaptToPreferenceChange(PropertyChangeEvent) |
| // */ |
| // public void adaptToPreferenceChange(PropertyChangeEvent event) { |
| // |
| // if (event.getProperty().equals(SOURCE_VERSION)) { |
| // Object value= event.getNewValue(); |
| // |
| // if (value instanceof String) { |
| // String s= (String) value; |
| // |
| // for (Iterator<ISourceVersionDependent> it= fVersionDependentRules.iterator(); it.hasNext();) { |
| // ISourceVersionDependent dependent= (ISourceVersionDependent) it.next(); |
| // dependent.setSourceVersion(s); |
| // } |
| // } |
| // |
| // } else if (super.affectsBehavior(event)) { |
| // super.adaptToPreferenceChange(event); |
| // } |
| // } |
| } |