From: robekras Date: Sat, 29 Dec 2012 17:00:08 +0000 (+0100) Subject: 1) Fixed issue #347: Syntax highlight doesn't like apostrophe in heredoc. X-Git-Url: http://git.phpeclipse.com 1) Fixed issue #347: Syntax highlight doesn't like apostrophe in heredoc. 2) Changed some little source formatting issues, to enhance readability. Signed-off-by: robekras --- diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/parser/Scanner.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/parser/Scanner.java index 20039eb..56a9b14 100644 --- a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/parser/Scanner.java +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/parser/Scanner.java @@ -1536,73 +1536,92 @@ public class Scanner implements IScanner, ITerminalSymbols { if (getNextChar('=')) return TokenName.REMAINDER_EQUAL; return TokenName.REMAINDER; + case '<': { int oldPosition = currentPosition; + try { currentCharacter = source[currentPosition++]; } catch (IndexOutOfBoundsException e) { currentPosition = oldPosition; return TokenName.LESS; } + switch (currentCharacter) { - case '=': - return TokenName.LESS_EQUAL; - case '>': - return TokenName.NOT_EQUAL; - case '<': - if (getNextChar('=')) - return TokenName.LEFT_SHIFT_EQUAL; - if (getNextChar('<')) { - currentCharacter = source[currentPosition++]; - while (Character.isWhitespace(currentCharacter)) { - currentCharacter = source[currentPosition++]; - } - int heredocStart = currentPosition - 1; - int heredocLength = 0; - if (isPHPIdentifierStart(currentCharacter)) { - currentCharacter = source[currentPosition++]; - } else { - return TokenName.ERROR; + case '=': + return TokenName.LESS_EQUAL; + + case '>': + return TokenName.NOT_EQUAL; + + case '<': + if (getNextChar ('=')) { + return TokenName.LEFT_SHIFT_EQUAL; } - while (isPHPIdentifierPart(currentCharacter)) { + + if (getNextChar('<')) { currentCharacter = source[currentPosition++]; - } - heredocLength = currentPosition - heredocStart - - 1; - // heredoc end-tag determination - boolean endTag = true; - char ch; - do { - ch = source[currentPosition++]; - if (ch == '\r' || ch == '\n') { - if (recordLineSeparator) { - pushLineSeparator(); - } else { - currentLine = null; - } - for (int i = 0; i < heredocLength; i++) { - if (source[currentPosition + i] != source[heredocStart - + i]) { - endTag = false; - break; + + while (Character.isWhitespace(currentCharacter)) { + currentCharacter = source[currentPosition++]; + } + + int heredocStart = currentPosition - 1; + int heredocLength = 0; + + if (isPHPIdentifierStart (currentCharacter)) { + currentCharacter = source[currentPosition++]; + } + else { + return TokenName.ERROR; + } + + while (isPHPIdentifierPart(currentCharacter)) { + currentCharacter = source[currentPosition++]; + } + + heredocLength = currentPosition - heredocStart - 1; + + // heredoc end-tag determination + boolean endTag = true; + char ch; + do { + ch = source[currentPosition++]; + + if (ch == '\r' || ch == '\n') { + if (recordLineSeparator) { + pushLineSeparator(); + } + else { + currentLine = null; + } + + for (int i = 0; i < heredocLength; i++) { + if (source[currentPosition + i] != source[heredocStart + i]) { + endTag = false; + break; + } + } + + if (endTag) { + currentPosition += heredocLength - 1; + currentCharacter = source[currentPosition++]; + break; // do...while loop + } + else { + endTag = true; } } - if (endTag) { - currentPosition += heredocLength - 1; - currentCharacter = source[currentPosition++]; - break; // do...while loop - } else { - endTag = true; - } - } - } while (true); - return TokenName.HEREDOC; + } while (true); + + return TokenName.HEREDOC; + } + return TokenName.LEFT_SHIFT; } - return TokenName.LEFT_SHIFT; + currentPosition = oldPosition; + return TokenName.LESS; } - currentPosition = oldPosition; - return TokenName.LESS; - } + case '>': { int test; if ((test = getNextChar('=', '>')) == 0) diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/FastJavaPartitionScanner.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/FastJavaPartitionScanner.java index a4239ba..867d0cb 100644 --- a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/FastJavaPartitionScanner.java +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/FastJavaPartitionScanner.java @@ -29,57 +29,43 @@ public class FastJavaPartitionScanner implements IPartitionTokenScanner, IPHPPartitions { // states - private static final int PHP = 0; - - private static final int SINGLE_LINE_COMMENT = 1; - - private static final int MULTI_LINE_COMMENT = 2; - - private static final int PHPDOC = 3; - - private static final int STRING_DQ = 4; - - private static final int STRING_SQ = 5; - - private static final int STRING_HEREDOC = 6; - - // beginning of prefixes and postfixes - private static final int NONE = 0; - - private static final int BACKSLASH = 1; // postfix for STRING_DQ and - // CHARACTER - - private static final int SLASH = 2; // prefix for SINGLE_LINE or MULTI_LINE - // or - - // JAVADOC - - private static final int SLASH_STAR = 3; // prefix for MULTI_LINE_COMMENT - // or - - // JAVADOC - - private static final int SLASH_STAR_STAR = 4; // prefix for - // MULTI_LINE_COMMENT - - // or JAVADOC - - private static final int STAR = 5; // postfix for MULTI_LINE_COMMENT or - - // JAVADOC - - private static final int CARRIAGE_RETURN = 6; // postfix for STRING_DQ, - - // CHARACTER and - // SINGLE_LINE_COMMENT - - // private static final int HEREDOC = 7; - + + private static enum PartState { + PHP, + SINGLE_LINE_COMMENT, + MULTI_LINE_COMMENT, + PHPDOC, + STRING_DQ, + STRING_SQ, + STRING_HEREDOC, + }; + + private static enum ScanState { + NONE, + BACKSLASH, // postfix for STRING_DQ and CHARACTER + SLASH, // prefix for SINGLE_LINE or MULTI_LINE or JAVADOC + SLASH_STAR, // prefix for MULTI_LINE_COMMENT or JAVADOC + SLASH_STAR_STAR, // prefix for MULTI_LINE_COMMENT or JAVADOC + STAR, // postfix for MULTI_LINE_COMMENT or JAVADOC + CARRIAGE_RETURN, // postfix for STRING_DQ, CHARACTER and SINGLE_LINE_COMMENT + LESS, // Found a '<' + LESS_LESS, // Found a '<<' + LESS_LESS_LESS, // Found a '<<<' + HEREDOC_ID, // Found a '<<<' and scanning ID (till and of id, which is a ' ') + HEREDOC, // Found a '<<<' and ID + HEREDOC_ID_END, // Searching the heredoc end ID + }; + + /** The heredoc ID string */ + private String fHeredocId; + + /** The possible heredoc ID string which is read right after a new line. Ends with a ';' and should + * match the heredoc ID string fHeredocId + */ + private String fHeredocIdEnd; + /** The scanner. */ - private final BufferedDocumentScanner fScanner = new BufferedDocumentScanner( - 1000); // faster - - // implementation + private final BufferedDocumentScanner fScanner = new BufferedDocumentScanner (1000); // faster implementation /** The offset of the last returned token. */ private int fTokenOffset; @@ -88,11 +74,11 @@ public class FastJavaPartitionScanner implements IPartitionTokenScanner, private int fTokenLength; /** The state of the scanner. */ - private int fState; + private PartState fState; /** The last significant characters read. */ - private int fLast; - + private ScanState fLast; + /** The amount of characters already read on first call to nextToken(). */ private int fPrefixLength; @@ -103,11 +89,14 @@ public class FastJavaPartitionScanner implements IPartitionTokenScanner, private int fJavaLength; - private final IToken[] fTokens = new IToken[] { new Token(null), - new Token(PHP_SINGLELINE_COMMENT), - new Token(PHP_MULTILINE_COMMENT), new Token(PHP_PHPDOC_COMMENT), - new Token(PHP_STRING_DQ), new Token(PHP_STRING_SQ), - new Token(PHP_STRING_HEREDOC) }; + private final IToken[] fTokens = new IToken[] { + new Token (null), + new Token (PHP_SINGLELINE_COMMENT), + new Token (PHP_MULTILINE_COMMENT), + new Token (PHP_PHPDOC_COMMENT), + new Token (PHP_STRING_DQ), + new Token (PHP_STRING_SQ), + new Token (PHP_STRING_HEREDOC)}; public FastJavaPartitionScanner(boolean emulate) { fEmulate = emulate; @@ -117,478 +106,570 @@ public class FastJavaPartitionScanner implements IPartitionTokenScanner, this(false); } - /* + /** + * Emulate JavaPartitionScanner + * * @see org.eclipse.jface.text.rules.ITokenScanner#nextToken() */ - public IToken nextToken() { - - // emulate JavaPartitionScanner + public IToken nextToken() { if (fEmulate) { - if (fJavaOffset != -1 - && fTokenOffset + fTokenLength != fJavaOffset + fJavaLength) { + if ((fJavaOffset != -1) && (fTokenOffset + fTokenLength != fJavaOffset + fJavaLength)) { fTokenOffset += fTokenLength; - return fTokens[PHP]; - } else { + + return fTokens[PartState.PHP.ordinal()]; + } + else { fJavaOffset = -1; fJavaLength = 0; } } - fTokenOffset += fTokenLength; - fTokenLength = fPrefixLength; - + fTokenOffset += fTokenLength; // The new token offset is the offset of the previous partition + length of previous partition + fTokenLength = fPrefixLength; // The new partition is at least the length of the start of the new partition + while (true) { final int ch = fScanner.read(); - // characters switch (ch) { - case ICharacterScanner.EOF: - if (fTokenLength > 0) { - fLast = NONE; // ignore last - return preFix(fState, PHP, NONE, 0); - - } else { - fLast = NONE; - fPrefixLength = 0; - return Token.EOF; - } - - case '\r': - // emulate JavaPartitionScanner - if (!fEmulate && fLast != CARRIAGE_RETURN) { - fLast = CARRIAGE_RETURN; - fTokenLength++; - continue; - - } else { - + case ICharacterScanner.EOF: + if (fTokenLength > 0) { + fLast = ScanState.NONE; // ignore last + return preFix (fState, PartState.PHP, ScanState.NONE, 0); + } + else { + fLast = ScanState.NONE; + fPrefixLength = 0; + return Token.EOF; + } + + case '\r': // Found a carriage return + // emulate JavaPartitionScanner + if (!fEmulate && (fLast != ScanState.CARRIAGE_RETURN)) { + fLast = ScanState.CARRIAGE_RETURN; // Set to what we currently found + fTokenLength++; // and count the partition length + + continue; // Go for the next character to read + } + else { + switch (fState) { + case SINGLE_LINE_COMMENT: + if (fTokenLength > 0) { + IToken token = fTokens[fState.ordinal()]; + + // emulate JavaPartitionScanner + if (fEmulate) { + fTokenLength++; + fLast = ScanState.NONE; + fPrefixLength = 0; + } + else { + fLast = ScanState.CARRIAGE_RETURN; + fPrefixLength = 1; + } + + fState = PartState.PHP; + return token; + } + else { + consume(); + continue; + } + + default: + consume(); + continue; + } + } + + case '\n': // Found a line feed switch (fState) { - case SINGLE_LINE_COMMENT: - // case CHARACTER: - // case STRING_DQ: - // case STRING_SQ: - if (fTokenLength > 0) { - IToken token = fTokens[fState]; - - // emulate JavaPartitionScanner - if (fEmulate) { - fTokenLength++; - fLast = NONE; - fPrefixLength = 0; - } else { - fLast = CARRIAGE_RETURN; - fPrefixLength = 1; - } - - fState = PHP; - return token; - - } else { - consume(); + case SINGLE_LINE_COMMENT: // If we running within a single line comment, + return postFix (fState); // this is the end my friend + + case STRING_HEREDOC: // If we running within a heredoc string + fTokenLength++; // Count the character + fLast = ScanState.CARRIAGE_RETURN; // and state is still new line + continue; + + default: // If running anywhere else than on a single line comment + consume(); // count the length of the current partition continue; + } + + case '?': + if (fState == PartState.SINGLE_LINE_COMMENT) { + int nextch = fScanner.read(); + + if (nextch == '>') { + //

This is an example.

+ fTokenLength--; + fScanner.unread(); + fScanner.unread(); + + return postFix (fState); + } + else { + // bug #1404228: Crash on + if (nextch != ICharacterScanner.EOF) { + fScanner.unread(); + } } - - default: - consume(); - continue; } - } - - case '\n': - switch (fState) { - case SINGLE_LINE_COMMENT: - // case CHARACTER: - // case STRING_DQ: - // case STRING_SQ: - // assert(fTokenLength > 0); - return postFix(fState); - + default: - consume(); - continue; - } - - case '?': - if (fState == SINGLE_LINE_COMMENT) { - int nextch = fScanner.read(); - if (nextch == '>') { - //

This is an example.

- fTokenLength--; - fScanner.unread(); - fScanner.unread(); - return postFix(fState); - } else { - // bug #1404228: Crash on - if (nextch != ICharacterScanner.EOF) { - fScanner.unread(); + if (!fEmulate && (fLast == ScanState.CARRIAGE_RETURN)) { + switch (fState) { + case SINGLE_LINE_COMMENT: + // case CHARACTER: + // case STRING_DQ: + // case STRING_SQ: + ScanState last; + PartState newState; + + switch (ch) { + case '/': + last = ScanState.SLASH; + newState = PartState.PHP; + break; + + case '*': + last = ScanState.STAR; + newState = PartState.PHP; + break; + + case '\'': + last = ScanState.NONE; + newState = PartState.STRING_SQ; + break; + + case '"': + last = ScanState.NONE; + newState = PartState.STRING_DQ; + break; + + case '\r': + last = ScanState.CARRIAGE_RETURN; + newState = PartState.PHP; + break; + + case '\\': + last = ScanState.BACKSLASH; + newState = PartState.PHP; + break; + + default: + last = ScanState.NONE; + newState = PartState.PHP; + break; + } + + fLast = ScanState.NONE; // ignore fLast + return preFix (fState, newState, last, 1); + + default: + break; } } - } - - default: - if (!fEmulate && fLast == CARRIAGE_RETURN) { - switch (fState) { - case SINGLE_LINE_COMMENT: - // case CHARACTER: - // case STRING_DQ: - // case STRING_SQ: - int last; - int newState; - switch (ch) { - case '/': - last = SLASH; - newState = PHP; - break; - - case '*': - last = STAR; - newState = PHP; - break; + } - case '\'': - last = NONE; - newState = STRING_SQ; + // states + switch (fState) { + case PHP: + switch (ch) { + case '#': // Start of a single line comment + if (fTokenLength > 0) { + return preFix (PartState.PHP, PartState.SINGLE_LINE_COMMENT, ScanState.NONE, 1); + } + else { + preFix (PartState.PHP, PartState.SINGLE_LINE_COMMENT, ScanState.NONE, 1); + fTokenOffset += fTokenLength; + fTokenLength = fPrefixLength; + } break; - - case '"': - last = NONE; - newState = STRING_DQ; + + case '<': + if (fLast == ScanState.LESS) { + fTokenLength++; + fLast = ScanState.LESS_LESS; + } + else if (fLast == ScanState.LESS_LESS) { + if (fTokenLength - getLastLength(fLast) > 0) { // this is the start of a single line comment + return preFix (PartState.PHP, PartState.STRING_HEREDOC, ScanState.LESS_LESS_LESS, 3); + } + else { + preFix (PartState.PHP, PartState.STRING_HEREDOC, ScanState.LESS_LESS_LESS, 3); + fTokenOffset += fTokenLength; + fTokenLength = fPrefixLength; + } + } + else { + fTokenLength++; + fLast = ScanState.LESS; + } break; - - case '\r': - last = CARRIAGE_RETURN; - newState = PHP; + + case '/': // Start of single line comment? + if (fLast == ScanState.SLASH) { // If previous character was already a slash, + if (fTokenLength - getLastLength(fLast) > 0) { // this is the start of a single line comment + return preFix (PartState.PHP, PartState.SINGLE_LINE_COMMENT, ScanState.NONE, 2); + } + else { + preFix (PartState.PHP, PartState.SINGLE_LINE_COMMENT, ScanState.NONE, 2); + fTokenOffset += fTokenLength; + fTokenLength = fPrefixLength; + } + } + else { + fTokenLength++; + fLast = ScanState.SLASH; // We currently found a slash + } break; - - case '\\': - last = BACKSLASH; - newState = PHP; + + case '*': + if (fLast == ScanState.SLASH) { // If previous character was a slash + if (fTokenLength - getLastLength (fLast) > 0) { // this is the start of a comment /* + return preFix (PartState.PHP, PartState.MULTI_LINE_COMMENT, ScanState.SLASH_STAR, 2); + } + else { + preFix (PartState.PHP, PartState.MULTI_LINE_COMMENT, ScanState.SLASH_STAR, 2); + fTokenOffset += fTokenLength; + fTokenLength = fPrefixLength; + } + } + else { // No slash before the '*', so it's a normal character + consume (); + } break; - - default: - last = NONE; - newState = PHP; + + case '\'': // The start of a single quoted string + fLast = ScanState.NONE; // ignore fLast + + if (fTokenLength > 0) { + return preFix (PartState.PHP, PartState.STRING_SQ, ScanState.NONE, 1); + } + else { + preFix (PartState.PHP, PartState.STRING_SQ, ScanState.NONE, 1); + fTokenOffset += fTokenLength; + fTokenLength = fPrefixLength; + } break; - } - - fLast = NONE; // ignore fLast - return preFix(fState, newState, last, 1); - - default: - break; - } - } - } - - // states - switch (fState) { - case PHP: - switch (ch) { - case '#': - if (fTokenLength > 0) { - return preFix(PHP, SINGLE_LINE_COMMENT, NONE, 1); - } else { - preFix(PHP, SINGLE_LINE_COMMENT, NONE, 1); - fTokenOffset += fTokenLength; - fTokenLength = fPrefixLength; - break; - } - case '/': - if (fLast == SLASH) { - if (fTokenLength - getLastLength(fLast) > 0) { - return preFix(PHP, SINGLE_LINE_COMMENT, NONE, 2); - } else { - preFix(PHP, SINGLE_LINE_COMMENT, NONE, 2); - fTokenOffset += fTokenLength; - fTokenLength = fPrefixLength; + + case '"': // The start of a double quoted string + fLast = ScanState.NONE; // ignore fLast + + if (fTokenLength > 0) { + return preFix (PartState.PHP, PartState.STRING_DQ, ScanState.NONE, 1); + } + else { + preFix (PartState.PHP, PartState.STRING_DQ, ScanState.NONE, 1); + fTokenOffset += fTokenLength; + fTokenLength = fPrefixLength; + } break; - } - - } else { - fTokenLength++; - fLast = SLASH; - break; - } - - case '*': - if (fLast == SLASH) { - if (fTokenLength - getLastLength(fLast) > 0) - return preFix(PHP, MULTI_LINE_COMMENT, SLASH_STAR, - 2); - else { - preFix(PHP, MULTI_LINE_COMMENT, SLASH_STAR, 2); - fTokenOffset += fTokenLength; - fTokenLength = fPrefixLength; + + default: // Just a normal character with no special meaning + consume (); break; - } - - } else { - consume(); - break; } - - case '\'': - fLast = NONE; // ignore fLast - if (fTokenLength > 0) - return preFix(PHP, STRING_SQ, NONE, 1); - else { - preFix(PHP, STRING_SQ, NONE, 1); - fTokenOffset += fTokenLength; - fTokenLength = fPrefixLength; - break; - } - - case '"': - fLast = NONE; // ignore fLast - if (fTokenLength > 0) - return preFix(PHP, STRING_DQ, NONE, 1); - else { - preFix(PHP, STRING_DQ, NONE, 1); - fTokenOffset += fTokenLength; - fTokenLength = fPrefixLength; - break; - } - - default: - consume(); break; - } - break; - - case SINGLE_LINE_COMMENT: - consume(); - break; - - case PHPDOC: - switch (ch) { - case '/': - switch (fLast) { - case SLASH_STAR_STAR: - return postFix(MULTI_LINE_COMMENT); - - case STAR: - return postFix(PHPDOC); - - default: - consume(); - break; - } - break; - - case '*': - fTokenLength++; - fLast = STAR; - break; - - default: + + case SINGLE_LINE_COMMENT: // We are just running within a single line comment (started with // or #) consume(); break; - } - break; - - case MULTI_LINE_COMMENT: - switch (ch) { - case '*': - if (fLast == SLASH_STAR) { - fLast = SLASH_STAR_STAR; - fTokenLength++; - fState = PHPDOC; - } else { - fTokenLength++; - fLast = STAR; + + case PHPDOC: // We are just running within a php doc comment + switch (ch) { + case '/': + switch (fLast) { + case SLASH_STAR_STAR: + return postFix (PartState.MULTI_LINE_COMMENT); + + case STAR: + return postFix (PartState.PHPDOC); // Found the end of the php doc (multi line) comment + + default: + consume(); + break; + } + break; + + case '*': // Found a '*' + fTokenLength++; + fLast = ScanState.STAR; // Remember that we found a '*' + break; + + default: + consume(); + break; } break; - - case '/': - if (fLast == STAR) { - return postFix(MULTI_LINE_COMMENT); - } else { - consume(); - break; + + case MULTI_LINE_COMMENT: // We are currently running through a (possible) multi line comment + switch (ch) { + case '*': // and we found a '*' + if (fLast == ScanState.SLASH_STAR) { // If the previous characters have been a /* + fLast = ScanState.SLASH_STAR_STAR; + fTokenLength++; + fState = PartState.PHPDOC; + } + else { + fTokenLength++; + fLast = ScanState.STAR; + } + break; + + case '/': + if (fLast == ScanState.STAR) { + return postFix (PartState.MULTI_LINE_COMMENT); + } + else { + consume(); + break; + } + + default: + consume(); + break; } - - default: - consume(); - break; - } - break; - - case STRING_DQ: - switch (ch) { - case '\\': - fLast = (fLast == BACKSLASH) ? NONE : BACKSLASH; - fTokenLength++; break; - - case '\"': - if (fLast != BACKSLASH) { - return postFix(STRING_DQ); - - } else { - consume(); - break; + + case STRING_DQ: + switch (ch) { + case '\\': + fLast = (fLast == ScanState.BACKSLASH) ? ScanState.NONE : ScanState.BACKSLASH; + fTokenLength++; + break; + + case '\"': + if (fLast != ScanState.BACKSLASH) { + return postFix (PartState.STRING_DQ); + } + else { + consume(); + } + break; + + default: + consume(); + break; } - - default: - consume(); break; - } - break; - case STRING_SQ: - switch (ch) { - case '\\': - fLast = (fLast == BACKSLASH) ? NONE : BACKSLASH; - fTokenLength++; + + case STRING_SQ: + switch (ch) { + case '\\': + fLast = (fLast == ScanState.BACKSLASH) ? ScanState.NONE : ScanState.BACKSLASH; + fTokenLength++; + break; + + case '\'': + if (fLast != ScanState.BACKSLASH) { + return postFix (PartState.STRING_SQ); + } + else { + consume(); + } + break; + + default: + consume(); + break; + } break; - - case '\'': - if (fLast != BACKSLASH) { - return postFix(STRING_SQ); - - } else { - consume(); - break; + + case STRING_HEREDOC: // We are just running within a heredoc string + switch (fLast) { + case LESS_LESS_LESS: // The first time after we recognized the '<<<' + fLast = ScanState.HEREDOC_ID; // We do a scan of the heredoc id string + fHeredocId = ""; + fHeredocId += (char) ch; + fTokenLength++; + break; + + case HEREDOC_ID: // Scan the starting heredoc ID + if (ch == ' ') { + fLast = ScanState.HEREDOC; + fTokenLength++; + } + else { + fHeredocId += (char) ch; + fTokenLength++; + } + break; + + case CARRIAGE_RETURN: // We previously found a new line + fTokenLength++; + fHeredocIdEnd = ""; + fHeredocIdEnd += (char) ch; // Add the first character to the (possible) end ID + fLast = ScanState.HEREDOC_ID_END; // Go for scanning the (possible) end ID + break; + + case HEREDOC_ID_END: // We scan the (possible) end ID + if (ch == ';') { // End ID ends with an ';' + if (fHeredocId.compareTo (fHeredocIdEnd) == 0) { // If start ID and end ID matches. + return postFix (PartState.STRING_HEREDOC); // It's the end of a heredoc partition + } + else { + consume (); // Wrong end ID, so just eat the character + } + } + else { + fTokenLength++; // + fHeredocIdEnd += (char) ch; // Add the characther to the possible heredoc end ID + } + break; + + default: // Normally state NONE + consume (); // Eat the character + break; } - - default: - consume(); break; - } - break; - // case CHARACTER: - // switch (ch) { - // case '\\': - // fLast= (fLast == BACKSLASH) ? NONE : BACKSLASH; - // fTokenLength++; - // break; - // - // case '\'': - // if (fLast != BACKSLASH) { - // return postFix(CHARACTER); - // - // } else { - // consume(); - // break; - // } - // - // default: - // consume(); - // break; - // } - // break; - } + } // end of switch (fState) } } - private static final int getLastLength(int last) { + private static final int getLastLength (ScanState last) { switch (last) { - default: - return -1; - - case NONE: - return 0; - - case CARRIAGE_RETURN: - case BACKSLASH: - case SLASH: - case STAR: - return 1; - - case SLASH_STAR: - return 2; - - case SLASH_STAR_STAR: - return 3; + default: + return -1; + + case NONE: + return 0; + + case LESS: + case CARRIAGE_RETURN: + case BACKSLASH: + case SLASH: + case STAR: + return 1; + + case LESS_LESS: + case SLASH_STAR: + return 2; + + case SLASH_STAR_STAR: + return 3; + + case HEREDOC: + return 3; } } private final void consume() { - fTokenLength++; - fLast = NONE; + fTokenLength++; // Count the character + fLast = ScanState.NONE; // Reset scanner state to nothing special } - private final IToken postFix(int state) { + /** + * If we found the end of a partition, return the type of the partition which is currently finished + * + * @param state The type of partition we found the end for + * @return + */ + private final IToken postFix (PartState state) { fTokenLength++; - fLast = NONE; - fState = PHP; - fPrefixLength = 0; - return fTokens[state]; + fLast = ScanState.NONE; // Reset the scanner state + fState = PartState.PHP; // The type of the next partition is just PHP + fPrefixLength = 0; // and have no prefix length + + return fTokens[state.ordinal()]; // Return the type of partition for which we found the end } - private final IToken preFix(int state, int newState, int last, - int prefixLength) { - // emulate JavaPartitionScanner - if (fEmulate && state == PHP - && (fTokenLength - getLastLength(fLast) > 0)) { - fTokenLength -= getLastLength(fLast); - fJavaOffset = fTokenOffset; - fJavaLength = fTokenLength; - fTokenLength = 1; - fState = newState; - fPrefixLength = prefixLength; - fLast = last; - return fTokens[state]; - - } else { - fTokenLength -= getLastLength(fLast); - fLast = last; + /** + * If we find the prefix of a new partition, return the type of the previous partition + * + * @param state + * @param newState + * @param last + * @param prefixLength + * @return + */ + private final IToken preFix (PartState oldState, PartState newState, ScanState last, int prefixLength) { + if (fEmulate && // If we are in emulation run + (oldState == PartState.PHP) && + (fTokenLength - getLastLength (fLast) > 0)) { + + fTokenLength -= getLastLength (fLast); + fJavaOffset = fTokenOffset; + fJavaLength = fTokenLength; + fTokenLength = 1; + fState = newState; fPrefixLength = prefixLength; - IToken token = fTokens[state]; - fState = newState; + fLast = last; + + return fTokens[oldState.ordinal()]; + } + else { + fTokenLength -= getLastLength (fLast); // Set the length of the last token (partition) + fLast = last; // Remember the type of the type of the last partition + fPrefixLength = prefixLength; // Remember the length of the currently found start of new partition + fState = newState; // The type of the new partition we found + + IToken token = fTokens[oldState.ordinal()]; // Return the type of the old partition + return token; } } - private static int getState(String contentType) { - + private static PartState getState (String contentType) { if (contentType == null) - return PHP; + return PartState.PHP; - else if (contentType.equals(PHP_SINGLELINE_COMMENT)) - return SINGLE_LINE_COMMENT; + else if (contentType.equals (PHP_SINGLELINE_COMMENT)) + return PartState.SINGLE_LINE_COMMENT; - else if (contentType.equals(PHP_MULTILINE_COMMENT)) - return MULTI_LINE_COMMENT; + else if (contentType.equals (PHP_MULTILINE_COMMENT)) + return PartState.MULTI_LINE_COMMENT; - else if (contentType.equals(PHP_PHPDOC_COMMENT)) - return PHPDOC; + else if (contentType.equals (PHP_PHPDOC_COMMENT)) + return PartState.PHPDOC; - else if (contentType.equals(PHP_STRING_DQ)) - return STRING_DQ; + else if (contentType.equals (PHP_STRING_DQ)) + return PartState.STRING_DQ; - else if (contentType.equals(PHP_STRING_SQ)) - return STRING_SQ; + else if (contentType.equals (PHP_STRING_SQ)) + return PartState.STRING_SQ; - else if (contentType.equals(PHP_STRING_HEREDOC)) - return STRING_HEREDOC; - - // else if (contentType.equals(JAVA_CHARACTER)) - // return CHARACTER; + else if (contentType.equals (PHP_STRING_HEREDOC)) + return PartState.STRING_HEREDOC; else - return PHP; + return PartState.PHP; } - /* - * @see IPartitionTokenScanner#setPartialRange(IDocument, int, int, String, - * int) + /** + * @see IPartitionTokenScanner#setPartialRange (IDocument, int, int, String, int) + * + * @note Because of the PHP heredoc syntax we need to parse from the beginning of a heredoc partition, + * and not from anywhere in the middle. When not reading the start of the heredoc (and the correct heredoc start ID, + * we can't recognize the correct heredoc end ID. So we start if possible form the partitionOffset. + * */ - public void setPartialRange(IDocument document, int offset, int length, - String contentType, int partitionOffset) { - fScanner.setRange(document, offset, length); - setRange(document, offset, length); - fTokenOffset = partitionOffset; - fTokenLength = 0; - fPrefixLength = offset - partitionOffset; - fLast = NONE; - - if (offset == partitionOffset) { - // restart at beginning of partition - fState = PHP; - } else { - fState = getState(contentType); + public void setPartialRange (IDocument document, int offset, int length, String contentType, int partitionOffset) { + if (partitionOffset >= 0) { + fScanner.setRange (document, partitionOffset, length + (offset - partitionOffset)); + + fTokenOffset = partitionOffset; + fTokenLength = 0; + fPrefixLength = 0; + fLast = ScanState.NONE; + fState = PartState.PHP; // restart at beginning of partition + } + else { + fScanner.setRange (document, offset, length); + + fTokenOffset = partitionOffset; + fTokenLength = 0; + fPrefixLength = offset - partitionOffset; + fLast = ScanState.NONE; + + if (offset == partitionOffset) { + fState = PartState.PHP; // restart at beginning of partition + } + else { + fState = getState(contentType); + } } - // emulate JavaPartitionScanner if (fEmulate) { fJavaOffset = -1; @@ -596,16 +677,17 @@ public class FastJavaPartitionScanner implements IPartitionTokenScanner, } } - /* + /** * @see ITokenScanner#setRange(IDocument, int, int) */ - public void setRange(IDocument document, int offset, int length) { - fScanner.setRange(document, offset, length); - fTokenOffset = offset; - fTokenLength = 0; + public void setRange (IDocument document, int offset, int length) { + fScanner.setRange (document, offset, length); + + fTokenOffset = offset; + fTokenLength = 0; fPrefixLength = 0; - fLast = NONE; - fState = PHP; + fLast = ScanState.NONE; + fState = PartState.PHP; // emulate JavaPartitionScanner if (fEmulate) { diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/SmartSemicolonAutoEditStrategy.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/SmartSemicolonAutoEditStrategy.java index d0b0d8d..e0bc0c7 100644 --- a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/SmartSemicolonAutoEditStrategy.java +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/ui/text/SmartSemicolonAutoEditStrategy.java @@ -64,7 +64,7 @@ public class SmartSemicolonAutoEditStrategy implements IAutoEditStrategy { /** Char representation of a semicolon. */ private static final char SEMICHAR = ';'; - /** String represenattion of a opening brace. */ + /** String representation of a opening brace. */ private static final String BRACE = "{"; //$NON-NLS-1$ /** Char representation of a opening brace */ diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ui/text/JavaTextTools.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ui/text/JavaTextTools.java index 3616ccc..636a10f 100644 --- a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ui/text/JavaTextTools.java +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ui/text/JavaTextTools.java @@ -51,8 +51,12 @@ public class JavaTextTools implements IPHPPartitions { // JSPScriptScanner.JSP_DEFAULT, // JSPScriptScanner.JSP_BRACKET }; private final static String[] LEGAL_CONTENT_TYPES = new String[] { - PHP_PHPDOC_COMMENT, PHP_MULTILINE_COMMENT, PHP_SINGLELINE_COMMENT, - PHP_STRING_DQ, PHP_STRING_SQ, PHP_STRING_HEREDOC }; + PHP_PHPDOC_COMMENT, + PHP_MULTILINE_COMMENT, + PHP_SINGLELINE_COMMENT, + PHP_STRING_DQ, + PHP_STRING_SQ, + PHP_STRING_HEREDOC }; // private static XMLPartitionScanner HTML_PARTITION_SCANNER = null; diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ui/text/PHPSourceViewerConfiguration.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ui/text/PHPSourceViewerConfiguration.java index 6a47790..df918f6 100644 --- a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ui/text/PHPSourceViewerConfiguration.java +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/ui/text/PHPSourceViewerConfiguration.java @@ -648,22 +648,30 @@ public class PHPSourceViewerConfiguration extends SourceViewerConfiguration { * (non-Javadoc) Method declared on SourceViewerConfiguration */ public String[] getConfiguredContentTypes(ISourceViewer sourceViewer) { - return new String[] { IDocument.DEFAULT_CONTENT_TYPE, + return new String[] { + IDocument.DEFAULT_CONTENT_TYPE, PHPPartitionScanner.PHP_SCRIPTING_AREA, - IPHPPartitions.HTML, IPHPPartitions.HTML_MULTILINE_COMMENT, + IPHPPartitions.HTML, + IPHPPartitions.HTML_MULTILINE_COMMENT, IPHPPartitions.PHP_PARTITIONING, IPHPPartitions.PHP_SINGLELINE_COMMENT, IPHPPartitions.PHP_MULTILINE_COMMENT, IPHPPartitions.PHP_PHPDOC_COMMENT, - IPHPPartitions.PHP_STRING_DQ, IPHPPartitions.PHP_STRING_SQ, - IPHPPartitions.PHP_STRING_HEREDOC, IPHPPartitions.CSS, + IPHPPartitions.PHP_STRING_DQ, + IPHPPartitions.PHP_STRING_SQ, + IPHPPartitions.PHP_STRING_HEREDOC, + IPHPPartitions.CSS, IPHPPartitions.CSS_MULTILINE_COMMENT, - IPHPPartitions.JAVASCRIPT, IPHPPartitions.JS_MULTILINE_COMMENT, - IPHPPartitions.SMARTY, IPHPPartitions.SMARTY_MULTILINE_COMMENT, - - XMLPartitionScanner.XML_PI, XMLPartitionScanner.XML_COMMENT, - XMLPartitionScanner.XML_DECL, XMLPartitionScanner.XML_TAG, + IPHPPartitions.JAVASCRIPT, + IPHPPartitions.JS_MULTILINE_COMMENT, + IPHPPartitions.SMARTY, + IPHPPartitions.SMARTY_MULTILINE_COMMENT, + + XMLPartitionScanner.XML_PI, + XMLPartitionScanner.XML_COMMENT, + XMLPartitionScanner.XML_DECL, + XMLPartitionScanner.XML_TAG, XMLPartitionScanner.XML_ATTRIBUTE, XMLPartitionScanner.XML_CDATA, @@ -677,9 +685,12 @@ public class PHPSourceViewerConfiguration extends SourceViewerConfiguration { } public String[] getConfiguredHTMLContentTypes() { - return new String[] { XMLPartitionScanner.XML_PI, - XMLPartitionScanner.XML_COMMENT, XMLPartitionScanner.XML_DECL, - XMLPartitionScanner.XML_TAG, XMLPartitionScanner.XML_ATTRIBUTE, + return new String[] { + XMLPartitionScanner.XML_PI, + XMLPartitionScanner.XML_COMMENT, + XMLPartitionScanner.XML_DECL, + XMLPartitionScanner.XML_TAG, + XMLPartitionScanner.XML_ATTRIBUTE, XMLPartitionScanner.XML_CDATA, XMLPartitionScanner.DTD_INTERNAL, @@ -689,16 +700,21 @@ public class PHPSourceViewerConfiguration extends SourceViewerConfiguration { } public String[] getConfiguredPHPContentTypes() { - return new String[] { IDocument.DEFAULT_CONTENT_TYPE, + return new String[] { + IDocument.DEFAULT_CONTENT_TYPE, IPHPPartitions.PHP_PARTITIONING, IPHPPartitions.PHP_SINGLELINE_COMMENT, IPHPPartitions.PHP_MULTILINE_COMMENT, IPHPPartitions.PHP_PHPDOC_COMMENT, - IPHPPartitions.PHP_STRING_DQ, IPHPPartitions.PHP_STRING_SQ, - IPHPPartitions.PHP_STRING_HEREDOC, IPHPPartitions.CSS, + IPHPPartitions.PHP_STRING_DQ, + IPHPPartitions.PHP_STRING_SQ, + IPHPPartitions.PHP_STRING_HEREDOC, + IPHPPartitions.CSS, IPHPPartitions.CSS_MULTILINE_COMMENT, - IPHPPartitions.JAVASCRIPT, IPHPPartitions.JS_MULTILINE_COMMENT, - IPHPPartitions.SMARTY, IPHPPartitions.SMARTY_MULTILINE_COMMENT, }; + IPHPPartitions.JAVASCRIPT, + IPHPPartitions.JS_MULTILINE_COMMENT, + IPHPPartitions.SMARTY, + IPHPPartitions.SMARTY_MULTILINE_COMMENT, }; } /* @@ -716,46 +732,34 @@ public class PHPSourceViewerConfiguration extends SourceViewerConfiguration { */ public IContentAssistant getContentAssistant(ISourceViewer sourceViewer) { ContentAssistant assistant = new ContentAssistant(); - IContentAssistProcessor processor = new HTMLCompletionProcessor( - getEditor()); - assistant - .setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer)); - assistant.setContentAssistProcessor(processor, IPHPPartitions.HTML); - assistant.setContentAssistProcessor(processor, - IPHPPartitions.HTML_MULTILINE_COMMENT); - - assistant.setContentAssistProcessor(processor, IPHPPartitions.CSS); - assistant.setContentAssistProcessor(processor, - IPHPPartitions.CSS_MULTILINE_COMMENT); - assistant.setContentAssistProcessor(processor, - IPHPPartitions.JAVASCRIPT); - assistant.setContentAssistProcessor(processor, - IPHPPartitions.JS_MULTILINE_COMMENT); + IContentAssistProcessor processor = new HTMLCompletionProcessor (getEditor()); + assistant.setDocumentPartitioning (getConfiguredDocumentPartitioning(sourceViewer)); + assistant.setContentAssistProcessor (processor, IPHPPartitions.HTML); + assistant.setContentAssistProcessor (processor, IPHPPartitions.HTML_MULTILINE_COMMENT); + + assistant.setContentAssistProcessor (processor, IPHPPartitions.CSS); + assistant.setContentAssistProcessor (processor, IPHPPartitions.CSS_MULTILINE_COMMENT); + assistant.setContentAssistProcessor (processor, IPHPPartitions.JAVASCRIPT); + assistant.setContentAssistProcessor (processor, IPHPPartitions.JS_MULTILINE_COMMENT); // TODO define special smarty partition content assist - assistant.setContentAssistProcessor(processor, IPHPPartitions.SMARTY); - assistant.setContentAssistProcessor(processor, - IPHPPartitions.SMARTY_MULTILINE_COMMENT); + assistant.setContentAssistProcessor (processor, IPHPPartitions.SMARTY); + assistant.setContentAssistProcessor (processor, IPHPPartitions.SMARTY_MULTILINE_COMMENT); - assistant.setContentAssistProcessor(processor, - PHPDocumentPartitioner.PHP_TEMPLATE_DATA); + assistant.setContentAssistProcessor (processor, PHPDocumentPartitioner.PHP_TEMPLATE_DATA); String[] htmlTypes = getConfiguredHTMLContentTypes(); + for (int i = 0; i < htmlTypes.length; i++) { assistant.setContentAssistProcessor(processor, htmlTypes[i]); } - processor = new PHPCompletionProcessor(getEditor()); + processor = new PHPCompletionProcessor (getEditor()); - assistant.setContentAssistProcessor(processor, - PHPDocumentPartitioner.PHP_SCRIPT_CODE); - assistant.setContentAssistProcessor(processor, - IPHPPartitions.PHP_PARTITIONING); - assistant.setContentAssistProcessor(processor, - IPHPPartitions.PHP_STRING_DQ); - assistant.setContentAssistProcessor(processor, - IPHPPartitions.PHP_STRING_SQ); - assistant.setContentAssistProcessor(processor, - IPHPPartitions.PHP_STRING_HEREDOC); - - assistant.setContentAssistProcessor(new PHPDocCompletionProcessor( + assistant.setContentAssistProcessor (processor, PHPDocumentPartitioner.PHP_SCRIPT_CODE); + assistant.setContentAssistProcessor (processor, IPHPPartitions.PHP_PARTITIONING); + assistant.setContentAssistProcessor (processor, IPHPPartitions.PHP_STRING_DQ); + assistant.setContentAssistProcessor (processor, IPHPPartitions.PHP_STRING_SQ); + assistant.setContentAssistProcessor (processor, IPHPPartitions.PHP_STRING_HEREDOC); + + assistant.setContentAssistProcessor (new PHPDocCompletionProcessor( getEditor()), IPHPPartitions.PHP_PHPDOC_COMMENT); // assistant.enableAutoActivation(true); // assistant.setAutoActivationDelay(500); @@ -766,11 +770,9 @@ public class PHPSourceViewerConfiguration extends SourceViewerConfiguration { // assistant.setContextInformationPopupBackground( // PHPEditorEnvironment.getPHPColorProvider().getColor( // new RGB(150, 150, 0))); - ContentAssistPreference.configure(assistant, getPreferenceStore()); - assistant - .setContextInformationPopupOrientation(ContentAssistant.CONTEXT_INFO_ABOVE); - assistant - .setInformationControlCreator(getInformationControlCreator(sourceViewer)); + ContentAssistPreference.configure (assistant, getPreferenceStore()); + assistant.setContextInformationPopupOrientation (ContentAssistant.CONTEXT_INFO_ABOVE); + assistant.setInformationControlCreator (getInformationControlCreator(sourceViewer)); return assistant; } diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/phpeditor/PHPEditor.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/phpeditor/PHPEditor.java index cfc3857..cc75aed 100644 --- a/net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/phpeditor/PHPEditor.java +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/phpeditor/PHPEditor.java @@ -5203,17 +5203,16 @@ public abstract class PHPEditor extends AbstractDecoratedTextEditor implements throws BadLocationException { IRegion line = document.getLineInformationOfOffset(lineOffset); - ITypedRegion[] linePartitioning = document.computePartitioning( - lineOffset, line.getLength()); + ITypedRegion[] linePartitioning = document.computePartitioning (lineOffset, line.getLength()); List segmentation = new ArrayList(); + for (int i = 0; i < linePartitioning.length; i++) { - if (IPHPPartitions.PHP_STRING_DQ.equals(linePartitioning[i] - .getType())) { - segmentation.add(linePartitioning[i]); - } else if (IPHPPartitions.PHP_STRING_HEREDOC - .equals(linePartitioning[i].getType())) { - segmentation.add(linePartitioning[i]); + if (IPHPPartitions.PHP_STRING_DQ.equals (linePartitioning[i].getType())) { + segmentation.add (linePartitioning[i]); + } + else if (IPHPPartitions.PHP_STRING_HEREDOC.equals (linePartitioning[i].getType())) { + segmentation.add (linePartitioning[i]); } } diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/phpeditor/PHPSyntaxRdr.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/phpeditor/PHPSyntaxRdr.java index 110aeb3..ee1fbd6 100644 --- a/net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/phpeditor/PHPSyntaxRdr.java +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/phpeditor/PHPSyntaxRdr.java @@ -71,7 +71,7 @@ public class PHPSyntaxRdr { //private static boolean hasXMLFileBeenRead = true; // The following variable is used to hold the syntax from - // the suers custom file - if that file should be changed, + // the users custom file - if that file should be changed, // then all entries in this variable should be removed from // the word list, reread from the file and then reinserted. private static ArrayList userdefsyntaxdata; diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/phpeditor/php/PHPPartitionScanner.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/phpeditor/php/PHPPartitionScanner.java index 55d8783..a3a6e0f 100644 --- a/net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/phpeditor/php/PHPPartitionScanner.java +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpeclipse/phpeditor/php/PHPPartitionScanner.java @@ -306,27 +306,35 @@ public class PHPPartitionScanner implements IPartitionTokenScanner { return false; } + /** + * Read until HEREDOC ends + * + * @return + */ private boolean readUntilEscapedHEREDOC() { - // search until heredoc ends try { char ch; StringBuffer buf = new StringBuffer(); char[] heredocIdent; + if (position >= end) { return false; } + ch = document.getChar(position++); - // #1493165 start + + while (ch == ' ') { if (position >= end) { return false; } ch = document.getChar(position++); } - // #1493165 end + if (!Scanner.isPHPIdentifierStart(ch)) { return false; } + while (Scanner.isPHPIdentifierPart(ch)) { buf.append(ch); if (position >= end) { @@ -334,27 +342,35 @@ public class PHPPartitionScanner implements IPartitionTokenScanner { } ch = document.getChar(position++); } + heredocIdent = buf.toString().toCharArray(); + while (true) { if (position >= end) { return false; } - ch = document.getChar(position++); - if (ch == '\n') { // heredoc could end after a newline + + ch = document.getChar (position++); // Get the next character from file + + if (ch == '\n') { // heredoc could end after a newline int pos = 0; + while (true) { - if (position >= end) { - return false; + if (position >= end) { // If we are at the end of file + return false; // Return } - if (pos == heredocIdent.length) { - return true; + + if (pos == heredocIdent.length) { // If the found length equals the length of heredoc id + return true; // we found the end of heredoc } - ch = document.getChar(position++); // ignore escaped - // character - if (ch != heredocIdent[pos]) { - break; + + ch = document.getChar (position++); // Ignore escaped character + + if (ch != heredocIdent[pos]) { // If current character doesn't match the heredoc id + break; // break the heredoc end search } - pos++; + + pos++; // Character matched the heredoc id so far } } }