| Index: editor/tools/plugins/com.google.dart.tools.ui/src/com/google/dart/tools/ui/internal/text/functions/FastDartPartitionScanner.java
|
| ===================================================================
|
| --- editor/tools/plugins/com.google.dart.tools.ui/src/com/google/dart/tools/ui/internal/text/functions/FastDartPartitionScanner.java (revision 5470)
|
| +++ editor/tools/plugins/com.google.dart.tools.ui/src/com/google/dart/tools/ui/internal/text/functions/FastDartPartitionScanner.java (working copy)
|
| @@ -1,5 +1,5 @@
|
| /*
|
| - * Copyright (c) 2011, the Dart project authors.
|
| + * Copyright (c) 2012, 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
|
| @@ -26,6 +26,10 @@
|
| * multi-line strings, in addition to the default.
|
| */
|
| public class FastDartPartitionScanner implements IPartitionTokenScanner, DartPartitions {
|
| + /**
|
| + * Values of the enumeration <code>ScannerState</code> represent the states that the scanner can
|
| + * be in. The scanner is essentially a state machine with these states.
|
| + */
|
| private enum ScannerState {
|
| //
|
| // Final states corresponding to partitions.
|
| @@ -115,6 +119,64 @@
|
| }
|
| }
|
|
|
| + /**
|
| + * Instances of the class <code>TokenData</code> represent a single token that was scanned. The
|
| + * scanner scans all of the tokens, creating a linked list of tokens to be returned by
|
| + * {@link FastDartPartitionScanner#nextToken()}.
|
| + */
|
| + private static class TokenData {
|
| + /**
|
| + * Create a new token that comes after the given token in the linked list with the given
|
| + * information.
|
| + *
|
| + * @param previous the token before the new token in the linked list
|
| + * @param token the token being added to the list
|
| + * @param tokenOffset the offset of the token in the source
|
| + * @param tokenLength the length of the token
|
| + * @return the token that was created
|
| + */
|
| + public static TokenData following(TokenData previous, IToken token, int tokenOffset,
|
| + int tokenLength) {
|
| + TokenData data = new TokenData(token, tokenOffset, tokenLength);
|
| + previous.next = data;
|
| + return data;
|
| + }
|
| +
|
| + /**
|
| + * The token value being represented.
|
| + */
|
| + private IToken token;
|
| +
|
| + /**
|
| + * The offset of the token in the source.
|
| + */
|
| + private int tokenOffset;
|
| +
|
| + /**
|
| + * The length of the token.
|
| + */
|
| + private int tokenLength;
|
| +
|
| + /**
|
| + * The data for the token following this token.
|
| + */
|
| + private TokenData next;
|
| +
|
| + /**
|
| + * Initialize a newly created node in the linked list of token data to store the information
|
| + * associated with the given token.
|
| + *
|
| + * @param token the token being represented by this node
|
| + * @param tokenOffset the offset of the token in the source
|
| + * @param tokenLength the length of the token
|
| + */
|
| + public TokenData(IToken token, int tokenOffset, int tokenLength) {
|
| + this.token = token;
|
| + this.tokenOffset = tokenOffset;
|
| + this.tokenLength = tokenLength;
|
| + }
|
| + }
|
| +
|
| private static IToken CODE_TOKEN = new Token(null);
|
| private static IToken SINGLE_LINE_COMMENT_TOKEN = new Token(DART_SINGLE_LINE_COMMENT);
|
| private static IToken MULTI_LINE_COMMENT_TOKEN = new Token(DART_MULTI_LINE_COMMENT);
|
| @@ -183,30 +245,161 @@
|
| */
|
| private int commentDepth = 0;
|
|
|
| + /**
|
| + * The head of the linked list, which always points to the data for the token that was last
|
| + * returned.
|
| + */
|
| + private TokenData currentToken;
|
| +
|
| + /**
|
| + * A flag used to determine whether debugging output should be produced.
|
| + */
|
| + private static final boolean DEBUG = false;
|
| +
|
| + /**
|
| + * Initialize a newly created scanner.
|
| + */
|
| public FastDartPartitionScanner() {
|
| - // create the scanner
|
| + super();
|
| }
|
|
|
| @Override
|
| public int getTokenLength() {
|
| - return tokenLength;
|
| + return currentToken.tokenLength;
|
| }
|
|
|
| @Override
|
| public int getTokenOffset() {
|
| - return tokenOffset;
|
| + return currentToken.tokenOffset;
|
| }
|
|
|
| @Override
|
| public IToken nextToken() {
|
| - // Uncomment the code below for debugging output.
|
| -// IToken result = nextToken_internal();
|
| -// System.out.println(tokenOffset + " - " + (tokenOffset + tokenLength - 1) + " (" + tokenLength
|
| -// + ") : " + result.getData());
|
| -// return result;
|
| -// }
|
| -//
|
| -// public IToken nextToken_internal() {
|
| + currentToken = currentToken.next;
|
| + if (DEBUG) {
|
| + System.out.println(" " + currentToken.tokenOffset + " - "
|
| + + (currentToken.tokenOffset + currentToken.tokenLength - 1) + " ("
|
| + + currentToken.tokenLength + ") : " + currentToken.token.getData());
|
| + }
|
| + return currentToken.token;
|
| + }
|
| +
|
| + @Override
|
| + public void setPartialRange(IDocument document, int offset, int length, String contentType,
|
| + int partitionOffset) {
|
| + if (DEBUG) {
|
| + System.out.println("setPartialRange(?, " + offset + ", " + length + ", " + contentType + ", "
|
| + + partitionOffset + ")");
|
| + }
|
| + // Scan a multi-line string from the beginning, so that the active string delimiter gets set.
|
| + if (contentType != null && contentType.equals(DART_MULTI_LINE_STRING)) {
|
| + length += offset - partitionOffset;
|
| + offset = partitionOffset;
|
| + }
|
| + setRange(document, offset, length);
|
| + }
|
| +
|
| + @Override
|
| + public void setRange(IDocument document, int offset, int length) {
|
| + commentDepth = 0;
|
| + scanner.setRange(document, 0, document.getLength());
|
| + tokenOffset = 0;
|
| + tokenLength = 0;
|
| + prefixLength = 0;
|
| + scannerState = ScannerState.CODE;
|
| + stringState = null;
|
| + currentToken = buildData();
|
| + trimTokenData(offset, length);
|
| + }
|
| +
|
| + /**
|
| + * Advance to the next character in the input.
|
| + */
|
| + private void advance() {
|
| + tokenLength++;
|
| + scanner.read();
|
| + }
|
| +
|
| + /**
|
| + * Build the linked list of tokens representing the content of the entire document.
|
| + *
|
| + * @return a fake token that is logically the last token returned before any tokens have actually
|
| + * been returned
|
| + */
|
| + private TokenData buildData() {
|
| + if (DEBUG) {
|
| + System.out.println(" buildData()");
|
| + }
|
| + //
|
| + // Create a fake token so that the first invocation of nextToken() will return the real first
|
| + // token.
|
| + //
|
| + TokenData head = new TokenData(Token.UNDEFINED, 0, 0);
|
| + TokenData current = head;
|
| + while (current.token != Token.EOF) {
|
| + current = TokenData.following(current, parseToken(), tokenOffset, tokenLength);
|
| + }
|
| + current.next = current;
|
| + return head;
|
| + }
|
| +
|
| + /**
|
| + * Return the code-like scanner state to which the scanner should return at the end of the current
|
| + * state. This can either be {@link ScannerState#CODE} or {@link ScannerState#BLOCK_INTERPOLATION}
|
| + * , depending on whether the scanner is currently within a multi-line string.
|
| + *
|
| + * @return the code-like scanner state to which the scanner should return
|
| + */
|
| + private ScannerState getCodeLikeState() {
|
| + if (stringState == null) {
|
| + return ScannerState.CODE;
|
| + } else {
|
| + return ScannerState.BLOCK_INTERPOLATION;
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Return <code>true</code> if the given character is an end-of-line character.
|
| + *
|
| + * @param character the character being tested
|
| + * @return <code>true</code> if the given character is an end-of-line character
|
| + */
|
| + private boolean isEol(int character) {
|
| + return character == '\r' || character == '\n' || character == '\u2028' || character == '\u2029';
|
| + }
|
| +
|
| + /**
|
| + * Return <code>true</code> if the given character is a valid character within an identifier.
|
| + *
|
| + * @param character the character being tested
|
| + * @return <code>true</code> if the given character is a valid character within an identifier
|
| + */
|
| + private boolean isIdentifierChar(int character) {
|
| + return (character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z')
|
| + || (character >= '0' && character <= '9') || character == '_';
|
| + }
|
| +
|
| + /**
|
| + * Parse a single token from the input.
|
| + *
|
| + * @return the token that was parsed
|
| + */
|
| + private IToken parseToken() {
|
| + IToken result = parseToken_internal();
|
| + if (DEBUG) {
|
| + System.out.println(" " + tokenOffset + " - " + (tokenOffset + tokenLength - 1) + " ("
|
| + + tokenLength + ") : " + result.getData());
|
| + }
|
| + return result;
|
| + }
|
| +
|
| + /**
|
| + * Parse a single token from the input. This helper method exists so that debugging output can be
|
| + * produced in a single location.
|
| + *
|
| + * @return the token that was parsed
|
| + */
|
| + private IToken parseToken_internal() {
|
| tokenOffset += tokenLength;
|
| tokenLength = prefixLength;
|
| prefixLength = 0;
|
| @@ -297,7 +490,7 @@
|
| stringState = stringState.previous;
|
| scannerState = getCodeLikeState();
|
| return ScannerState.STRING.token;
|
| - } else if (currentChar == '\\') {
|
| + } else if (!stringState.raw && currentChar == '\\') {
|
| advance();
|
| advance();
|
| } else if (!stringState.raw && currentChar == '$') {
|
| @@ -451,70 +644,46 @@
|
| return Token.EOF;
|
| }
|
|
|
| - @Override
|
| - public void setPartialRange(IDocument document, int offset, int length, String contentType,
|
| - int partitionOffset) {
|
| - // Scan a multi-line string from the beginning, so that the active string delimiter gets set.
|
| - if (contentType != null && contentType.equals(DART_MULTI_LINE_STRING)) {
|
| - length += offset - partitionOffset;
|
| - offset = partitionOffset;
|
| - }
|
| - commentDepth = 0;
|
| - scanner.setRange(document, offset, length);
|
| - tokenOffset = partitionOffset;
|
| - tokenLength = 0;
|
| - prefixLength = offset - partitionOffset;
|
| -
|
| - if (offset == partitionOffset) {
|
| - // restart at beginning of partition
|
| - scannerState = ScannerState.CODE;
|
| - } else {
|
| - scannerState = getState(contentType);
|
| - if (scannerState == ScannerState.MULTI_LINE_COMMENT
|
| - || scannerState == ScannerState.DOC_COMMENT) {
|
| - commentDepth++;
|
| - }
|
| - }
|
| - stringState = null;
|
| - }
|
| -
|
| - @Override
|
| - public void setRange(IDocument document, int offset, int length) {
|
| - commentDepth = 0;
|
| - scanner.setRange(document, offset, length);
|
| - tokenOffset = offset;
|
| - tokenLength = 0;
|
| - prefixLength = 0;
|
| - scannerState = ScannerState.CODE;
|
| - stringState = null;
|
| - }
|
| -
|
| - private void advance() {
|
| - tokenLength++;
|
| - scanner.read();
|
| - }
|
| -
|
| /**
|
| - * Return the code-like scanner state to which the scanner should return at the end of the current
|
| - * state. This can either be {@link ScannerState#CODE} or {@link ScannerState#BLOCK_INTERPOLATION}
|
| - * , depending on whether the scanner is currently within a multi-line string.
|
| + * Adjust the linked list of tokens so that only those that encompass the given range of
|
| + * characters will be returned.
|
| *
|
| - * @return the code-like scanner state to which the scanner should return
|
| + * @param offset the offset of the first character to be included in a token
|
| + * @param length the number of characters to be included in tokens
|
| */
|
| - private ScannerState getCodeLikeState() {
|
| - if (stringState == null) {
|
| - return ScannerState.CODE;
|
| - } else {
|
| - return ScannerState.BLOCK_INTERPOLATION;
|
| + private void trimTokenData(int offset, int length) {
|
| + //
|
| + // Skip over any tokens that should not be returned. currentToken is assumed to be the fake
|
| + // token created before the first real token.
|
| + //
|
| + TokenData nextToken = currentToken.next;
|
| + while (nextToken != nextToken.next && nextToken.next.tokenOffset <= offset) {
|
| + nextToken = nextToken.next;
|
| }
|
| + currentToken.next = nextToken;
|
| + //
|
| + // Fix the token offset of the first token to match the requested offset.
|
| + //
|
| + TokenData firstToken = currentToken.next;
|
| + if (firstToken.tokenOffset < offset) {
|
| + firstToken.tokenLength = firstToken.tokenLength - (offset - firstToken.tokenOffset);
|
| + firstToken.tokenOffset = offset;
|
| + }
|
| + //
|
| + // Trim the tail of the list to cover only the requested length.
|
| + //
|
| + int totalLength = nextToken.tokenLength;
|
| + while (nextToken != nextToken.next && totalLength < length) {
|
| + nextToken = nextToken.next;
|
| + totalLength += nextToken.tokenLength;
|
| + }
|
| + if (totalLength > length) {
|
| + nextToken.tokenLength = nextToken.tokenLength - (tokenLength - length);
|
| + TokenData lastToken = nextToken.next;
|
| + while (lastToken != lastToken.next) {
|
| + lastToken = lastToken.next;
|
| + }
|
| + nextToken.next = lastToken;
|
| + }
|
| }
|
| -
|
| - private boolean isEol(int character) {
|
| - return character == '\r' || character == '\n' || character == '\u2028' || character == '\u2029';
|
| - }
|
| -
|
| - private boolean isIdentifierChar(int character) {
|
| - return (character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z')
|
| - || (character >= '0' && character <= '9') || character == '_';
|
| - }
|
| }
|
|
|